<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Ocarina — manual</title>
    <link>https://mojo-molotov.github.io/from-ocarina-to-igor/</link>
    <description>Recent content on Ocarina — manual</description>
    <generator>Hugo</generator>
    <language>en-US</language>
    <lastBuildDate>Thu, 04 Jun 2026 21:25:43 +0200</lastBuildDate>
    <atom:link href="https://mojo-molotov.github.io/from-ocarina-to-igor/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>00.01 — Ecosystem cartography</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/00-big-picture/01-ecosystem-map/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/00-big-picture/01-ecosystem-map/</guid>
      <description>&lt;h1 id=&#34;0001ecosystem-cartography&#34;&gt;00.01 — Ecosystem cartography&lt;a class=&#34;anchor&#34; href=&#34;#0001ecosystem-cartography&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;p&gt;The Ocarina ecosystem is &lt;strong&gt;six public repos&lt;/strong&gt; under the &lt;a href=&#34;https://github.com/mojo-molotov&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;mojo-molotov&lt;/code&gt;&lt;/a&gt; GitHub account. They cover &lt;strong&gt;five distinct roles&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Role&lt;/th&gt;&#xA;          &lt;th&gt;Repo&lt;/th&gt;&#xA;          &lt;th&gt;Form&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;Framework&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Python library on PyPI&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Public System Under Test (SUT)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;igoristan&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;React + Vike SPA, GitHub Pages&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Coordination backend&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;tests-workers&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Vercel Edge API + Upstash Redis&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Example suites&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina-example&lt;/code&gt; (canonical), &lt;code&gt;ocarina-with-ai-example&lt;/code&gt; (AI)&lt;/td&gt;&#xA;          &lt;td&gt;Python e2e suites driving Igoristan / CURA&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Public documentation&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina-holy-book&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;VitePress site (EN + FR + RU), PDFs, AI skills&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;the-big-picture&#34;&gt;The big picture&lt;a class=&#34;anchor&#34; href=&#34;#the-big-picture&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────────────────────┐&#xA;│                              OCARINA (BIG PICTURE)                              │&#xA;└─────────────────────────────────────────────────────────────────────────────────┘&#xA;&#xA;   ┌──────────────────────────────┐         ┌──────────────────────────────────┐&#xA;   │  [PACKAGE]  ocarina          │ ──────► │  [PACKAGE]  ocarina-example      │&#xA;   │  Python framework 3.14+      │   pip   │  e2e suite against the Igoristan │&#xA;   │  ROP / DSL / orchestration   │ install │  (Selenium + Firefox + Redis)    │&#xA;   └──────────────────────────────┘         └──────────────────────────────────┘&#xA;            │  │                                       │  ▲   │&#xA;            │  │                                       │  │  uses&#xA;            │  │ pip install ocarina                   │  │  IGOR_API_KEY&#xA;            │  ▼                                       ▼  │   │&#xA;            │  ┌────────────────────────────────────┐  drives │&#xA;            │  │ [PACKAGE]  ocarina-with-ai-example │  ┌──────┴──────────────────┐&#xA;            │  │ e2e suite against CURA Healthcare  │  │ [WEBSITE]  igoristan    │&#xA;            │  │ Claude Code, 99% machine-written   │  │ React 19 + Vike         │&#xA;            │  └────────────────────────────────────┘  │ GitHub Pages            │&#xA;            │              │                           └──────┬──────────────────┘&#xA;            │              │ drives                           │ fetches OTP&#xA;            │              ▼                                  ▼&#xA;            │   ┌──────────────────────────────┐    ┌──────────────────────────────┐&#xA;            │   │ [WEBSITE]  CURA Healthcare   │    │ [BACKEND]  tests-workers     │&#xA;            │   │ Heroku, open-source PHP      │    │ Vercel Edge Functions        │&#xA;            │   └──────────────────────────────┘    │ + Upstash Redis              │&#xA;            │                                       │ /api/otp, /otp-history,      │&#xA;            │                                       │ /api/corsicadex              │&#xA;            │                                       └──────────────────────────────┘&#xA;            │                                                 ▲&#xA;            │                                                 │ x-api-key&#xA;            │                                                 │ (IGOR_API_KEY ≡ API_SECRET)&#xA;            │                                                 │&#xA;            ▼&#xA;   ┌──────────────────────────────┐&#xA;   │  [DOCS]  ocarina-holy-book   │&#xA;   │  VitePress EN + FR + RU      │&#xA;   │  AI skills, PDFs, llms.txt   │&#xA;   │  GitHub Pages                │&#xA;   └──────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;ocarina&lt;/code&gt; → example suites&lt;/strong&gt;: plain pip dependency (&lt;code&gt;pip install ocarina&lt;/code&gt;).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Example suites → SUT (Igoristan / CURA)&lt;/strong&gt;: browser automation through Selenium.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Igoristan suite ↔ &lt;code&gt;tests-workers&lt;/code&gt;&lt;/strong&gt;: HTTP calls (OTP, Corsicadex). Shared secret: &lt;code&gt;IGOR_API_KEY&lt;/code&gt; client-side ≡ &lt;code&gt;API_SECRET&lt;/code&gt; server-side.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;responsibilities&#34;&gt;Responsibilities&lt;a class=&#34;anchor&#34; href=&#34;#responsibilities&#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;Role&lt;/th&gt;&#xA;          &lt;th&gt;Contract&lt;/th&gt;&#xA;          &lt;th&gt;Constraint&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Framework&lt;/td&gt;&#xA;          &lt;td&gt;Ships the DSL, the orchestration, the driver pool, the reporters&lt;/td&gt;&#xA;          &lt;td&gt;Stays small, auditable, no hidden deps&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Public SUT&lt;/td&gt;&#xA;          &lt;td&gt;A deliberately chaotic playground (random errors, OTP, finicky forms)&lt;/td&gt;&#xA;          &lt;td&gt;Lives on GitHub Pages forever, no heavy backend&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Coordination backend&lt;/td&gt;&#xA;          &lt;td&gt;Coordinates workers on OTP, surfaces data for parallel tests&lt;/td&gt;&#xA;          &lt;td&gt;Stateless code, state in Redis (Upstash)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Example suites&lt;/td&gt;&#xA;          &lt;td&gt;Show how Ocarina actually gets used — without AI (canonical) and with AI (CURA)&lt;/td&gt;&#xA;          &lt;td&gt;Public, runnable, complete, in CI&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Documentation&lt;/td&gt;&#xA;          &lt;td&gt;The why, the how, and the AI part&lt;/td&gt;&#xA;          &lt;td&gt;EN + FR + RU (Corso-Russian tradition), LLM-friendly files like &lt;code&gt;llms.txt&lt;/code&gt;, plus PDFs&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;three-licenses&#34;&gt;Three licenses&lt;a class=&#34;anchor&#34; href=&#34;#three-licenses&#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;License&lt;/th&gt;&#xA;          &lt;th&gt;Where&lt;/th&gt;&#xA;          &lt;th&gt;Why&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;MIT&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina&lt;/code&gt;, &lt;code&gt;ocarina-example&lt;/code&gt;, &lt;code&gt;ocarina-with-ai-example&lt;/code&gt;, &lt;code&gt;ocarina-holy-book&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Python code or docs. Transmissible as-is. Sovereign. See &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/11-independence/&#34;&gt;&lt;code&gt;../11-independence/&lt;/code&gt;&lt;/a&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;ISC&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;tests-workers&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Vercel Edge scaffold default. Kept as-is.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;em&gt;(unlicensed)&lt;/em&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;igoristan&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Public demo app. Treat it as WTFPL.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;deliberate-friction-between-repos&#34;&gt;Deliberate friction between repos&lt;a class=&#34;anchor&#34; href=&#34;#deliberate-friction-between-repos&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;The design is &lt;strong&gt;intentionally asymmetric&lt;/strong&gt; along certain axes. The friction is the testing tool:&lt;/p&gt;</description>
    </item>
    <item>
      <title>01.01 — Flipping the problem</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/01-philosophy/01-flip-the-problem/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/01-philosophy/01-flip-the-problem/</guid>
      <description>&lt;h1 id=&#34;0101flipping-the-problem&#34;&gt;01.01 — Flipping the problem&lt;a class=&#34;anchor&#34; href=&#34;#0101flipping-the-problem&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;the-observation&#34;&gt;The observation&lt;a class=&#34;anchor&#34; href=&#34;#the-observation&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Every modern e2e testing framework was built on one implicit assumption: there&amp;rsquo;s a &lt;strong&gt;barrier&lt;/strong&gt; between &lt;em&gt;people who code&lt;/em&gt; and &lt;em&gt;people who define tests&lt;/em&gt;. That barrier gets taken for granted — methodologically, organizationally, sometimes contractually.&lt;/p&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Most testing frameworks were built in a world where the barrier between &amp;ldquo;those who code&amp;rdquo; and &amp;ldquo;those who define tests&amp;rdquo; was real and structural.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;p&gt;The two &lt;em&gt;historic&lt;/em&gt; answers to that assumption show up in the Holy Book:&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.01 — Ocarina&#39;s technical identity</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/01-identity/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/01-identity/</guid>
      <description>&lt;h1 id=&#34;0201ocarinas-technical-identity&#34;&gt;02.01 — Ocarina&amp;rsquo;s technical identity&lt;a class=&#34;anchor&#34; href=&#34;#0201ocarinas-technical-identity&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;pyprojecttoml&#34;&gt;&lt;code&gt;pyproject.toml&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#pyprojecttoml&#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-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[project]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;name = &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;ocarina&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version = &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;1.1.10&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;description = &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Websites test framework for Igor&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;requires-python = &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;gt;=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;authors = [{ name=&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Igor Casanova&amp;#34;&lt;/span&gt;, email=&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt; }]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;license = &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;MIT&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;license-files = [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;LICEN[CS]E*&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;readme = &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;README.md&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;dependencies = [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;python-docx&amp;gt;=1.2.0&amp;#34;&lt;/span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;version = &amp;quot;1.1.10&amp;quot;&lt;/code&gt;&lt;/strong&gt;: &lt;em&gt;stable 1.x&lt;/em&gt;, not a pre-release.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;requires-python = &amp;quot;&amp;gt;=3.14&amp;quot;&lt;/code&gt;&lt;/strong&gt;: PEP 695 generic typing everywhere.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;dependencies = [&amp;quot;python-docx&amp;gt;=1.2.0&amp;quot;]&lt;/code&gt;&lt;/strong&gt;: &lt;strong&gt;one&lt;/strong&gt; runtime dependency. The rest lives in &lt;code&gt;dev&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;license = &amp;quot;MIT&amp;quot;&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;description = &amp;quot;Websites test framework for Igor&amp;quot;&lt;/code&gt;&lt;/strong&gt;: not &amp;ldquo;for everyone,&amp;rdquo; not &amp;ldquo;for humans,&amp;rdquo; explicitly &amp;ldquo;for Igor.&amp;rdquo; Lines up with &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/01-philosophy/05-political-stance/&#34;&gt;&lt;code&gt;../01-philosophy/05-political-stance.md&lt;/code&gt;&lt;/a&gt;: &amp;ldquo;It&amp;rsquo;s &lt;em&gt;my&lt;/em&gt; car.&amp;rdquo;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;dev-dependencies&#34;&gt;&lt;code&gt;dev&lt;/code&gt; dependencies&lt;a class=&#34;anchor&#34; href=&#34;#dev-dependencies&#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-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[dependency-groups]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;dev = [&#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;ruff&amp;gt;=0.15.0&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;mypy&amp;gt;=1.17.0&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;mypy-extensions&amp;gt;=1.1.0&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;typing-extensions&amp;gt;=4.14.0&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;pytest&amp;gt;=9.0.0&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;pytest-cov&amp;gt;=7.0.0&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;hypothesis&amp;gt;=6.151.0&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;pytest-mypy-plugins&amp;gt;=3.1.0&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;allure-pytest&amp;gt;=2.15.3&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;allure-python-commons&amp;gt;=2.15.3&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;pre-commit&amp;gt;=4.5.1&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;syrupy&amp;gt;=5.1.0&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;selenium&amp;gt;=4.40.0&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;playwright&amp;gt;=1.60.0&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;twine&amp;gt;=6.2.0&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;build&amp;gt;=1.4.2&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;prysk&amp;gt;=0.20.0&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;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Tool&lt;/th&gt;&#xA;          &lt;th&gt;Role in Ocarina&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;ruff&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Linter + formatter (replaces flake8 + isort + black). &lt;code&gt;select = [&amp;quot;ALL&amp;quot;]&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;mypy&lt;/code&gt; (+ extensions)&lt;/td&gt;&#xA;          &lt;td&gt;Type-checker. &lt;code&gt;strict = true&lt;/code&gt; (see &lt;code&gt;mypy.ini&lt;/code&gt;).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;pytest&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Unit-test runner (the framework is itself tested).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;pytest-cov&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Test coverage (of the framework itself). Configured in &lt;code&gt;pyproject.toml#tool.coverage&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;hypothesis&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Property-based testing (PBT)&lt;/em&gt; → &lt;code&gt;test_invariants_properties.py&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;pytest-mypy-plugins&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Static&lt;/strong&gt; typing tests (checks expected &lt;em&gt;mypy&lt;/em&gt; errors and type-inference tests with &lt;code&gt;reveal_type&lt;/code&gt;).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;allure-pytest&lt;/code&gt; / &lt;code&gt;allure-python-commons&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Allure report (deployed to GH Pages).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;pre-commit&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Local git hooks (ruff-format).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;syrupy&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Snapshot testing&lt;/em&gt; (output of &lt;code&gt;pretty_print_results&lt;/code&gt;, &lt;code&gt;results_to_json&lt;/code&gt;).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;selenium&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Present in &lt;code&gt;dev&lt;/code&gt; because Ocarina &lt;strong&gt;doesn&amp;rsquo;t depend&lt;/strong&gt; on Selenium to work; it just ships a Selenium &lt;em&gt;adapter&lt;/em&gt; so the framework is immediately operational out of box.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;playwright&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Same logic: a second shipped &lt;em&gt;adapter&lt;/em&gt; (since &lt;code&gt;1.1.3&lt;/code&gt;). Ocarina now drives &lt;strong&gt;both&lt;/strong&gt; Selenium and Playwright out of the box, behind the same ports.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;twine&lt;/code&gt; / &lt;code&gt;build&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PyPI publishing.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;prysk&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;CLI tests in the &lt;em&gt;cram&lt;/em&gt; (&lt;code&gt;.t&lt;/code&gt;) format. Successor to &lt;code&gt;cram&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;mypyini&#34;&gt;&lt;code&gt;mypy.ini&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#mypyini&#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-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;[mypy]&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:#50fa7b&#34;&gt;python_version&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;3.14&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:#50fa7b&#34;&gt;strict&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;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:#6272a4&#34;&gt;# * ... Allow missing annotations (type inference is cool)&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:#50fa7b&#34;&gt;disallow_incomplete_defs&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;false&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;# * ... Allow missing annotations (type inference is cool)&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:#50fa7b&#34;&gt;disallow_untyped_defs&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;strict = true&lt;/code&gt;&lt;/strong&gt;: enables &lt;code&gt;--warn-redundant-casts&lt;/code&gt;, &lt;code&gt;--warn-unused-ignores&lt;/code&gt;, &lt;code&gt;--no-implicit-optional&lt;/code&gt;, &lt;code&gt;--check-untyped-defs&lt;/code&gt;, etc.&lt;/li&gt;&#xA;&lt;li&gt;Two exceptions: &lt;code&gt;disallow_incomplete_defs&lt;/code&gt; and &lt;code&gt;disallow_untyped_defs&lt;/code&gt; are off. &lt;em&gt;mypy&lt;/em&gt;&amp;rsquo;s &lt;strong&gt;type inference&lt;/strong&gt; is good enough when explicit annotations don&amp;rsquo;t add value. Annotations live where they actually &lt;strong&gt;matter&lt;/strong&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;pyprojecttomltoolruff&#34;&gt;&lt;code&gt;pyproject.toml#tool.ruff&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#pyprojecttomltoolruff&#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-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[tool.ruff.lint]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;select = [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;ALL&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ignore = [&#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;ANN002&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;ANN003&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;ANN201&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;TRY003&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;C901&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;D203&amp;#34;&lt;/span&gt;,    &lt;span style=&#34;color:#6272a4&#34;&gt;# conflict with D211&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;D213&amp;#34;&lt;/span&gt;,    &lt;span style=&#34;color:#6272a4&#34;&gt;# conflict with D212&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;COM812&amp;#34;&lt;/span&gt;,  &lt;span style=&#34;color:#6272a4&#34;&gt;# conflict with the ruff formatter (auto-fix)&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;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[tool.ruff]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;exclude = [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;**/.venv/**&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;**/bin/**&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;**/__init__.py&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;**/__bypass_linter__&amp;#34;&lt;/span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;select = [&amp;quot;ALL&amp;quot;]&lt;/code&gt;&lt;/strong&gt;: every &lt;em&gt;ruff&lt;/em&gt; rule on (~800).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Short ignore list&lt;/strong&gt;: six rules, each one justified.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;B008&lt;/code&gt; must NEVER be ignored&lt;/strong&gt; (&amp;quot;&lt;em&gt;&lt;code&gt;# &amp;quot;B008&amp;quot;,  # * ... NEVER ignore this rule without knowing very well what you are doing: https://docs.astral.sh/ruff/rules/function-call-in-default-argument/&lt;/code&gt;&lt;/em&gt;&amp;quot;). Note left so the past mistake doesn&amp;rsquo;t repeat.&lt;/li&gt;&#xA;&lt;li&gt;**&lt;code&gt;**/**bypass_linter**&lt;/code&gt;**: directory convention for explicitly un-linted code (handy for internal experimental modules).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;pyprojecttomltoolpytestini_options&#34;&gt;&lt;code&gt;pyproject.toml#tool.pytest.ini_options&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#pyprojecttomltoolpytestini_options&#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-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;testpaths = [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;tests&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python_files = [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;test_*.py&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;norecursedirs = [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;.*&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;__pycache__&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;log_cli = &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;log_cli_level = &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;DEBUG&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;log_cli_format = &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;%(asctime)s [%(levelname)s] %(name)s: %(message)s&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;log_cli_date_format = &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;%Y-%m-%d %H:%M:%S&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;addopts = &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&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;--cov=src&#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;--cov-branch&#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;--cov-report=term-missing&#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;--cov-report=html&#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;--cov-report=xml:coverage.xml&#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;&amp;#34;&amp;#34;&lt;/span&gt;&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;Setting&lt;/th&gt;&#xA;          &lt;th&gt;Effect&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;log_cli = true&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;All test logs surface in the CLI (useful for scenarios that go through &lt;code&gt;ILogger&lt;/code&gt;).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--cov=src&lt;/code&gt; + &lt;code&gt;--cov-branch&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Branch coverage, not just line coverage.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--cov-report=html&lt;/code&gt; + &lt;code&gt;xml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Three outputs: terminal, local HTML, XML for CI ingestion.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;pyprojecttomltoolcoverage&#34;&gt;&lt;code&gt;pyproject.toml#tool.coverage&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#pyprojecttomltoolcoverage&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Full detail at &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/04-internal-tests/07-coverage-policy/&#34;&gt;&lt;code&gt;../04-internal-tests/07-coverage-policy.md&lt;/code&gt;&lt;/a&gt;. Short version: anything that&amp;rsquo;s pure &lt;em&gt;shape&lt;/em&gt; (custom_types, ports, errors), anything that needs a real browser (Selenium adapters), and anything covered by cram tests (cli/store, cli/builder) is explicitly omitted. Otherwise the metrics &lt;strong&gt;lie&lt;/strong&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.03.01 — The Result[T] type</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/03-railway/01-result/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/03-railway/01-result/</guid>
      <description>&lt;h1 id=&#34;020301the-resultt-type&#34;&gt;02.03.01 — The &lt;code&gt;Result[T]&lt;/code&gt; type&lt;a class=&#34;anchor&#34; href=&#34;#020301the-resultt-type&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/railway/result.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/railway/result.py&lt;/code&gt;&lt;/a&gt; — &lt;strong&gt;zero dependencies&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; dataclasses &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; dataclass, field&#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;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; TypeGuard, final&#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;&#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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_BaseResult&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;&amp;#34;&amp;#34;Base class ensuring both Ok and Fail have error attribute for type narrowing.&amp;#34;&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;    error: Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@final&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@dataclass(frozen&lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;Ok&lt;/span&gt;[T](_BaseResult):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    value: T&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error: &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@final&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@dataclass(frozen&lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;Fail&lt;/span&gt;(_BaseResult):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error: Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; field(default_factory&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt;: Exception(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Unknown error&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;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Result[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Ok[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; Fail&#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;&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;is_ok&lt;/span&gt;[T](result: Result[T]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; TypeGuard[Ok[T]]:&#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;return&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;isinstance&lt;/span&gt;(result, Ok)&#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;&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;is_fail&lt;/span&gt;[T](result: Result[T]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; TypeGuard[Fail]:&#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;return&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;isinstance&lt;/span&gt;(result, Fail)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;six-design-decisions-dissected&#34;&gt;Six design decisions, dissected&lt;a class=&#34;anchor&#34; href=&#34;#six-design-decisions-dissected&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;1-_baseresult-as-a-common-parent&#34;&gt;1. &lt;code&gt;_BaseResult&lt;/code&gt; as a common parent&lt;a class=&#34;anchor&#34; href=&#34;#1-_baseresult-as-a-common-parent&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_BaseResult&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error: Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One job: let &lt;em&gt;mypy&lt;/em&gt; &lt;strong&gt;read &lt;code&gt;result.error&lt;/code&gt; without narrowing first&lt;/strong&gt;. Without the base, you&amp;rsquo;d have to write:&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.04.01 — The validate → assert_that → execute → raise_if_invalid flow</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/04-invariants/01-validate-flow/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/04-invariants/01-validate-flow/</guid>
      <description>&lt;h1 id=&#34;020401the-validate--assert_that--execute--raise_if_invalid-flow&#34;&gt;02.04.01 — The &lt;code&gt;validate → assert_that → execute → raise_if_invalid&lt;/code&gt; flow&lt;a class=&#34;anchor&#34; href=&#34;#020401the-validate--assert_that--execute--raise_if_invalid-flow&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/invariants/validate.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/invariants/validate.py&lt;/code&gt;&lt;/a&gt; (entry-point) + &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/invariants/internals/validation_chain.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/invariants/internals/validation_chain.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;the-state-machine&#34;&gt;The state machine&lt;a class=&#34;anchor&#34; href=&#34;#the-state-machine&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;diagram&#34;&gt;Diagram&lt;a class=&#34;anchor&#34; href=&#34;#diagram&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;validate(value: T)&#xA;       │&#xA;       ▼&#xA;ValidationStartBlock[T]&#xA;       │&#xA;       └─ assert_that(predicate, *, msg=None)&#xA;              │&#xA;              ▼&#xA;       ValidationAssertBlock[T]   ← state accepting several continuations&#xA;              │&#xA;              ├─► assert_that(P)     [loop, AND-chainable, see dedicated diagram]&#xA;              ├─► otherwise(P)       [OR, see 03-otherwise-any-of.md]&#xA;              ├─► then(U, name=...)  [value swap, see 04-then-chain-of-validations.md]&#xA;              └─► execute()          → _ValidationResult&#xA;                                          │&#xA;                                          ├─ is_valid: bool&#xA;                                          ├─ errors: Sequence[InvariantViolationError]&#xA;                                          ├─ validated_values: Sequence[Any]&#xA;                                          └─ raise_if_invalid() : None | raise AggregateInvariantViolationError&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;assert_that-loop&#34;&gt;&lt;code&gt;assert_that&lt;/code&gt; loop&lt;a class=&#34;anchor&#34; href=&#34;#assert_that-loop&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;assert_that&lt;/code&gt; declares &lt;strong&gt;one&lt;/strong&gt; assertion on the current value. Multiple &lt;code&gt;assert_that&lt;/code&gt; calls chain — each adds another condition (logical AND, order preserved).&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.05.01 — Test[Driver]</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/05-orchestration/01-test/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/05-orchestration/01-test/</guid>
      <description>&lt;h1 id=&#34;020501testdriver&#34;&gt;02.05.01 — &lt;code&gt;Test[Driver]&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#020501testdriver&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/oc_test.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing/oc_test.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;signature&#34;&gt;Signature&lt;a class=&#34;anchor&#34; href=&#34;#signature&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@final&#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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;Test&lt;/span&gt;[Driver]:&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&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;font-style:italic&#34;&gt;self&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;*&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        name: TestName,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        test_id: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        test_scenario: TestScenario[Driver],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        pre_test_scenarios_fragments: Sequence[TestScenarioFragment[Driver]] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        post_test_scenarios_fragments: Sequence[TestScenarioFragment[Driver]] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        skipped: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;if&lt;/span&gt; test_id &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            test_id &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;test_id &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; test_id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_test_scenario &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; test_scenario&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_pre_test_scenarios_fragments &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; pre_test_scenarios_fragments &lt;span style=&#34;color:#ff79c6&#34;&gt;or&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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_post_test_scenarios_fragments &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; post_test_scenarios_fragments &lt;span style=&#34;color:#ff79c6&#34;&gt;or&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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_skipped &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; skipped&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Six parameters:&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Parameter&lt;/th&gt;&#xA;          &lt;th&gt;Type&lt;/th&gt;&#xA;          &lt;th&gt;Role&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;name&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestName&lt;/code&gt; (&lt;code&gt;str&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;Human label; appears in the report, and &lt;strong&gt;becomes the log file name&lt;/strong&gt; (hence subject to &lt;code&gt;is_valid_filename&lt;/code&gt;).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;test_id&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Stable identifier for &lt;code&gt;--only&lt;/code&gt; / &lt;code&gt;--exclude&lt;/code&gt;. If absent: takes &lt;code&gt;name&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;test_scenario&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestScenario[Driver]&lt;/code&gt; (alias = &lt;code&gt;Callable[[Driver, ILogger], Scenario[Driver]]&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Factory&lt;/em&gt; that builds the &lt;code&gt;Scenario&lt;/code&gt; at execution time.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;pre_test_scenarios_fragments&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Sequence[TestScenarioFragment[Driver]] | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;(driver, logger) -&amp;gt; TestChain&lt;/code&gt; functions executed &lt;strong&gt;before&lt;/strong&gt; the main scenario.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;post_test_scenarios_fragments&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Sequence[TestScenarioFragment[Driver]] | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;(driver, logger) -&amp;gt; TestChain&lt;/code&gt; functions executed &lt;strong&gt;after&lt;/strong&gt; the main scenario.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;skipped&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;If &lt;code&gt;True&lt;/code&gt;, the test is registered but not executed.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h3 id=&#34;1-final&#34;&gt;1. &lt;code&gt;@final&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#1-final&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;No user inheritance. Want a &amp;ldquo;special&amp;rdquo; test? &lt;em&gt;Compose&lt;/em&gt; via fragments or a scenario — don&amp;rsquo;t subclass.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.10.01 — WebDriversPool[Driver]</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/10-infra/01-drivers-pool/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/10-infra/01-drivers-pool/</guid>
      <description>&lt;h1 id=&#34;021001webdriverspooldriver&#34;&gt;02.10.01 — &lt;code&gt;WebDriversPool[Driver]&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#021001webdriverspooldriver&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/infra/drivers_pool.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/infra/drivers_pool.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Thread-safe driver pool, max concurrency enforced by semaphore, monitored async &lt;em&gt;warmup&lt;/em&gt;, clean &lt;em&gt;shutdown&lt;/em&gt;. No reuse — every &lt;code&gt;acquire()&lt;/code&gt; disposes its driver on the way out (clean state).&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;constructor&#34;&gt;Constructor&lt;a class=&#34;anchor&#34; href=&#34;#constructor&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@final&#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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;WebDriversPool&lt;/span&gt;[Driver]:&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&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;font-style:italic&#34;&gt;self&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        create_driver: Thunk[BuiltWebDriver[Driver]],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        max_size: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        warmup_timeout: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;float&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_create_driver &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; create_driver&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_pool: Queue[BuiltWebDriver[Driver]] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Queue(max_size)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_semaphore &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Semaphore(max_size)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_warmup_timeout &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            warmup_timeout &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; warmup_timeout &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;and&lt;/span&gt; warmup_timeout &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0.1&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;else&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;60.0&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;5&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;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Primitive&lt;/th&gt;&#xA;          &lt;th&gt;Role&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;Queue(max_size)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Queue of &lt;em&gt;pre-built&lt;/em&gt; available drivers.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;Semaphore(max_size)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Guarantees that &lt;strong&gt;at most &lt;code&gt;max_size&lt;/code&gt; drivers&lt;/strong&gt; live simultaneously (whether queued or acquired).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;warmup_timeout&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Max no-progress delay during warmup before raising &lt;code&gt;WarmupTimeoutError&lt;/code&gt;. Default 300s.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;acquire&#34;&gt;&lt;code&gt;acquire()&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#acquire&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@contextmanager&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;acquire&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Iterator[Driver]:&#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;try&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        driver, dispose &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_pool&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;get_nowait()&#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;except&lt;/span&gt; Empty:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_semaphore&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;acquire()&#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;try&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            driver, dispose &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_create_driver()&#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;except&lt;/span&gt; Exception:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_semaphore&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;release()&#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;raise&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;try&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;yield&lt;/span&gt; driver&#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;finally&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;with&lt;/span&gt; suppress(Exception):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dispose()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_semaphore&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;release()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;              acquire()&#xA;                  │&#xA;                  ▼&#xA;   ┌──────────────────────────────────┐&#xA;   │ pool.get_nowait()                │── OK ─► driver, dispose         ← warmup case&#xA;   └──────────────┬───────────────────┘&#xA;                  │ Empty&#xA;                  ▼&#xA;   ┌──────────────────────────────────┐&#xA;   │ sem.acquire()                    │  ← blocks if N drivers alive&#xA;   └──────────────┬───────────────────┘&#xA;                  ▼&#xA;   ┌──────────────────────────────────┐&#xA;   │ create_driver()                  │── raises ─► sem.release(); raise&#xA;   └──────────────┬───────────────────┘&#xA;                  ▼&#xA;   ┌──────────────────────────────────┐&#xA;   │ yield driver                     │  ← caller uses it&#xA;   └──────────────┬───────────────────┘&#xA;                  ▼&#xA;                finally :&#xA;                  with suppress : dispose()       ← driver disposed&#xA;                  sem.release()                   ← release a slot&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;1-no-reuse&#34;&gt;1. No reuse&lt;a class=&#34;anchor&#34; href=&#34;#1-no-reuse&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;Queue.get_nowait()&lt;/code&gt; &lt;em&gt;consumes&lt;/em&gt; the entry. Once pulled, the driver never goes back. At the end (&lt;code&gt;finally&lt;/code&gt;), it&amp;rsquo;s &lt;strong&gt;disposed&lt;/strong&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.11.01 — CliBuilder &#43; CliArg &#43; _SilentArgumentParser</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/01-cli-builder/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/01-cli-builder/</guid>
      <description>&lt;h1 id=&#34;021101clibuilder--cliarg--_silentargumentparser&#34;&gt;02.11.01 — &lt;code&gt;CliBuilder&lt;/code&gt; + &lt;code&gt;CliArg&lt;/code&gt; + &lt;code&gt;_SilentArgumentParser&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#021101clibuilder--cliarg--_silentargumentparser&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/cli/builder.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/opinionated/cli/builder.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Declarative overlay on top of &lt;code&gt;argparse&lt;/code&gt;. &lt;em&gt;Aggregates&lt;/em&gt; validation errors, &lt;em&gt;rewrites&lt;/em&gt; the help output on error, and &lt;em&gt;registers&lt;/em&gt; post-parse effects.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;_silentargumentparser&#34;&gt;&lt;code&gt;_SilentArgumentParser&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#_silentargumentparser&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_SilentArgumentParser&lt;/span&gt;(ArgumentParser):&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;error&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, message: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Never:&#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;&amp;#34;&amp;#34;Raise an error.&amp;#34;&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:#ff79c6&#34;&gt;raise&lt;/span&gt; ValueError(message)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The standard &lt;code&gt;ArgumentParser&lt;/code&gt; calls &lt;code&gt;sys.exit(2)&lt;/code&gt; directly on error. With that turd of a default, you &lt;strong&gt;can&amp;rsquo;t&lt;/strong&gt; intercept and you &lt;strong&gt;can&amp;rsquo;t&lt;/strong&gt; aggregate multiple errors.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;_SilentArgumentParser&lt;/code&gt; &lt;em&gt;reroutes&lt;/em&gt; errors as &lt;code&gt;ValueError&lt;/code&gt;. So &lt;code&gt;CliBuilder.parse&lt;/code&gt; can catch and aggregate them.&lt;/p&gt;</description>
    </item>
    <item>
      <title>03.01 — Effect, Thunk[T], Result[T]</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/01-effect-thunk-result/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/01-effect-thunk-result/</guid>
      <description>&lt;h1 id=&#34;0301effect-thunkt-resultt&#34;&gt;03.01 — &lt;code&gt;Effect&lt;/code&gt;, &lt;code&gt;Thunk[T]&lt;/code&gt;, &lt;code&gt;Result[T]&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#0301effect-thunkt-resultt&#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 lines in &lt;code&gt;custom_types/&lt;/code&gt; + one in &lt;code&gt;railway/&lt;/code&gt;. The whole DSL rides on them.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;triplet&#34;&gt;Triplet&lt;a class=&#34;anchor&#34; href=&#34;#triplet&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/ocarina/custom_types/effect.py&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:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Effect &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Callable[[], &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Effects &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;tuple&lt;/span&gt;[Effect, &lt;span style=&#34;color:#ff79c6&#34;&gt;...&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;# src/ocarina/custom_types/thunk.py&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:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Thunk[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Callable[[], T]&#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;# src/ocarina/railway/result.py&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:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Result[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Ok[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; Fail&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;Type&lt;/th&gt;&#xA;          &lt;th&gt;FP semantics&lt;/th&gt;&#xA;          &lt;th&gt;Definition&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;Effect&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Side effect (deferred)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;() -&amp;gt; None&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;Thunk[T]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Deferred &lt;em&gt;computation&lt;/em&gt; with value&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;() -&amp;gt; T&lt;/code&gt; (laziness + value)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;Result[T]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Computation&lt;/em&gt; that can fail&lt;/td&gt;&#xA;          &lt;td&gt;Discriminated union &lt;code&gt;Ok[T] | Fail&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;effect-vs-thunk-distinction&#34;&gt;Effect vs Thunk distinction&lt;a class=&#34;anchor&#34; href=&#34;#effect-vs-thunk-distinction&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;log_msg: Effect &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt;: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;)        &lt;span style=&#34;color:#6272a4&#34;&gt;# () -&amp;gt; None&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;get_42: Thunk[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;42&lt;/span&gt;                 &lt;span style=&#34;color:#6272a4&#34;&gt;# () -&amp;gt; int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fetch:  Thunk[Result[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;]] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt;: Ok(&lt;span style=&#34;color:#bd93f9&#34;&gt;42&lt;/span&gt;)     &lt;span style=&#34;color:#6272a4&#34;&gt;# () -&amp;gt; Result[int]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Function &lt;em&gt;returns&lt;/em&gt; something consumed elsewhere → &lt;code&gt;Thunk[T]&lt;/code&gt;.&lt;br&gt;&#xA;Otherwise → &lt;code&gt;Effect&lt;/code&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>04.01 — From the outside like a user strategy</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/04-internal-tests/01-strategy/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/04-internal-tests/01-strategy/</guid>
      <description>&lt;h1 id=&#34;0401from-the-outside-like-a-user-strategy&#34;&gt;04.01 — &amp;quot;&lt;em&gt;From the outside like a user&lt;/em&gt;&amp;quot; strategy&lt;a class=&#34;anchor&#34; href=&#34;#0401from-the-outside-like-a-user-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;The posture lives in the scenario tests&amp;rsquo; &lt;code&gt;conftest.py&lt;/code&gt;. No test cheats by peeking at internals.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;testsscenariosconftestpy&#34;&gt;&lt;code&gt;tests/scenarios/conftest.py&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#testsscenariosconftestpy&#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;Component&lt;/th&gt;&#xA;          &lt;th&gt;Role&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;FakeDriver&lt;/code&gt; (dataclass)&lt;/td&gt;&#xA;          &lt;td&gt;Minimal driver: &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;disposed&lt;/code&gt;. No Selenium method.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;make_built_driver()&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Factory &lt;code&gt;(FakeDriver, dispose)&lt;/code&gt;, i.e. the signature &lt;code&gt;WebDriversPool&lt;/code&gt; expects.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;make_pool(max_size=2)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;WebDriversPool[FakeDriver]&lt;/code&gt; ready to go.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;RecordingPOM(POMBase)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;POM that records its calls and can be configured to raise.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;acting(pom, step)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Full &lt;code&gt;ActionSuccess[RecordingPOM]&lt;/code&gt; (failure+success as no-ops).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;scenario_of(&amp;quot;ok&amp;quot;, &amp;quot;ok2&amp;quot;)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestScenario[FakeDriver]&lt;/code&gt; with N steps.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;failing_scenario(step=&amp;quot;boom&amp;quot;, exc=...)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;1-step scenario that raises.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;make_test(name, scenario=..., test_id=..., skipped=False)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Test[FakeDriver]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;make_suite(name, tests, pool=..., transient_errors=..., max_retries_per_test=...)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestSuite[FakeDriver]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;make_campaign(name, suites, max_workers=1)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestCampaign[FakeDriver]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;make_cycle(name=&amp;quot;cycle&amp;quot;, campaigns=..., smoke=..., mode=...)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestCycle[FakeDriver]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;run_chain(runner)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Helper that executes a &lt;code&gt;ChainRunner&lt;/code&gt; and returns the &lt;code&gt;ActionChain&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;fakedriver&#34;&gt;&lt;code&gt;FakeDriver&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#fakedriver&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@dataclass&#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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;FakeDriver&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    title: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;fake&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    disposed: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;False&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s &lt;strong&gt;all&lt;/strong&gt;.&#xA;&lt;strong&gt;Ocarina&amp;rsquo;s core calls no Selenium API.&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>05.01 — Igoristan stack</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/05-igoristan/01-stack/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/05-igoristan/01-stack/</guid>
      <description>&lt;h1 id=&#34;0501igoristan-stack&#34;&gt;05.01 — Igoristan stack&lt;a class=&#34;anchor&#34; href=&#34;#0501igoristan-stack&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;packagejson&#34;&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#packagejson&#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-json&#34; data-lang=&#34;json&#34;&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;&amp;#34;react&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^19.2.3&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;&amp;#34;react-dom&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^19.2.3&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;&amp;#34;vike&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^0.4.258&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;&amp;#34;vike-react&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^0.6.21&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;&amp;#34;valibot&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^1.2.0&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;&amp;#34;react-hook-form&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^7.71.1&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;&amp;#34;@hookform/resolvers&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^5.2.2&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;&amp;#34;react-dropzone&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^14.3.8&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;&amp;#34;lucide-react&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^0.562.0&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;&amp;#34;usehooks-ts&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^3.1.1&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;&amp;#34;throttleit&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^2.1.0&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;&amp;#34;uuid&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^13.0.0&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;&amp;#34;clsx&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^2.1.1&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;&amp;#34;class-variance-authority&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^0.7.1&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;&amp;#34;tailwind-merge&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^3.0.2&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;&amp;#34;tailwindcss-animate&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^1.0.7&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;&amp;#34;@radix-ui/react-slot&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^1.2.4&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;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Lib&lt;/th&gt;&#xA;          &lt;th&gt;Role&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;React 19.2&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;UI, hooks&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Vike 0.4&lt;/strong&gt; + &lt;code&gt;vike-react&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;SSG / SSR framework on top of Vite (formerly &amp;ldquo;&lt;em&gt;vite-plugin-ssr&lt;/em&gt;&amp;rdquo;)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Valibot 1.2&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;TS schema validation (lightweight Zod alternative)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;react-hook-form&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Form handling&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;react-dropzone&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Drag-and-drop upload component&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;lucide-react&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Icons&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;usehooks-ts&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Utility hooks (&lt;code&gt;useLocalStorage&lt;/code&gt;, etc.), used by &lt;code&gt;useAuth&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;throttleit&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Throttle&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;uuid&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;ID generation&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;clsx + tailwind-merge + class-variance-authority&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Tailwind utilities&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;devdependencies&#34;&gt;&lt;code&gt;devDependencies&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#devdependencies&#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-json&#34; data-lang=&#34;json&#34;&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;&amp;#34;vite&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^7.3.1&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;&amp;#34;@vitejs/plugin-react&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^5.1.2&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;&amp;#34;@tailwindcss/vite&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^4.1.3&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;&amp;#34;@tailwindcss/postcss&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^4.0.17&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;&amp;#34;tailwindcss&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^4.1.1&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;&amp;#34;terser&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^5.39.0&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;&amp;#34;vite-plugin-compression&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^0.5.1&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;&amp;#34;rollup-plugin-visualizer&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^5.14.0&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;&amp;#34;@qalisa/vike-plugin-sitemap&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^1.7.0&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:#ff79c6&#34;&gt;&amp;#34;typescript&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^6.0.3&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;&amp;#34;typescript-eslint&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^8.59.0&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;&amp;#34;eslint&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^9.18.0&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;&amp;#34;@eslint/js&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^9.39.2&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;&amp;#34;eslint-plugin-import-x&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^4.6.1&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;&amp;#34;eslint-plugin-perfectionist&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^4.6.0&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;&amp;#34;eslint-plugin-react&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^7.37.4&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;&amp;#34;eslint-plugin-react-hooks&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^5.2.0&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;&amp;#34;eslint-plugin-unused-imports&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^4.2.1&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;&amp;#34;globals&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^15.15.0&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:#ff79c6&#34;&gt;&amp;#34;prettier&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^3.5.3&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;&amp;#34;prettier-plugin-tailwindcss&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^0.6.11&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:#ff79c6&#34;&gt;&amp;#34;husky&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^9.1.7&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;&amp;#34;lint-staged&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^15.5.0&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;&amp;#34;commitizen&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^4.3.1&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;&amp;#34;@commitlint/config-conventional&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^19.8.0&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;&amp;#34;@commitlint/cz-commitlint&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^19.8.0&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;&amp;#34;is-ci&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^4.1.0&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;&amp;#34;editorconfig&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^2.0.1&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;&amp;#34;minimatch&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^10.1.1&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;&amp;#34;rimraf&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^6.0.1&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;&amp;#34;wireit&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^0.14.11&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;h2 id=&#34;engines&#34;&gt;&lt;code&gt;engines&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#engines&#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-json&#34; data-lang=&#34;json&#34;&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;&amp;#34;node&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^24.x&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;&amp;#34;pnpm&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;11.x&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;→ Node 24, pnpm 11. &lt;strong&gt;&lt;code&gt;packageManager: pnpm@11.x&lt;/code&gt;&lt;/strong&gt; locks the version.&lt;/p&gt;</description>
    </item>
    <item>
      <title>06.01 — Vercel Edge stack</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/06-tests-workers/01-stack-edge/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/06-tests-workers/01-stack-edge/</guid>
      <description>&lt;h1 id=&#34;0601vercel-edge-stack&#34;&gt;06.01 — Vercel Edge stack&lt;a class=&#34;anchor&#34; href=&#34;#0601vercel-edge-stack&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;packagejson&#34;&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#packagejson&#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-json&#34; data-lang=&#34;json&#34;&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;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;tests-workers&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;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;1.0.0&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;&amp;#34;scripts&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;&amp;#34;vercel-build&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;echo &amp;#39;Build skipped&amp;#39;&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:#ff79c6&#34;&gt;&amp;#34;packageManager&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;pnpm@11.2.2&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;&amp;#34;dependencies&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;&amp;#34;@upstash/redis&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^1.36.2&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;&amp;#34;otplib&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^13.2.1&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:#ff79c6&#34;&gt;&amp;#34;devDependencies&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;&amp;#34;@types/node&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^25.2.0&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;&amp;#34;next&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^16.1.6&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;&amp;#34;@vercel/node&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^5.5.28&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;&amp;#34;typescript&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^5.9.3&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:#ff79c6&#34;&gt;&amp;#34;license&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;ISC&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;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Lib&lt;/th&gt;&#xA;          &lt;th&gt;Role&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;&lt;code&gt;@upstash/redis&lt;/code&gt;&lt;/strong&gt; (runtime)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;REST&lt;/em&gt; Redis client (no TCP). Edge-compatible.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;otplib&lt;/code&gt;&lt;/strong&gt; (runtime)&lt;/td&gt;&#xA;          &lt;td&gt;RFC 6238 TOTP generation.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;next&lt;/code&gt;&lt;/strong&gt; (dev only)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Types&lt;/em&gt; only (&lt;code&gt;NextRequest&lt;/code&gt;). No Next.js app.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;@vercel/node&lt;/code&gt;&lt;/strong&gt; (dev only)&lt;/td&gt;&#xA;          &lt;td&gt;Vercel types.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;@types/node&lt;/code&gt;&lt;/strong&gt; (dev only)&lt;/td&gt;&#xA;          &lt;td&gt;Node 22+ types.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&lt;strong&gt;No&lt;/strong&gt; runtime dependency beyond &lt;code&gt;@upstash/redis&lt;/code&gt; + &lt;code&gt;otplib&lt;/code&gt;. Bundle is microscopic → cold start &amp;lt; 100ms.&lt;/p&gt;</description>
    </item>
    <item>
      <title>07.01 — Tree</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/01-tree/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/01-tree/</guid>
      <description>&lt;h1 id=&#34;0701tree&#34;&gt;07.01 — Tree&lt;a class=&#34;anchor&#34; href=&#34;#0701tree&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;src/&#xA;├── main.py                                                     # entry point (see README)&#xA;│&#xA;├── api/                                                        # external HTTP clients&#xA;│   ├── retrieve_dashboard_otp_code.py                          # filter + sort + pick OTP&#xA;│   ├── get_otp_history.py                                      # HTTP GET /api/otp-history&#xA;│   └── constants/endpoints.py                                  # URLs of tests-workers endpoints&#xA;│&#xA;├── caches/                                                     # in-memory L1 cache&#xA;│   ├── l1.py                                                   # dogpile.cache.memory TTL 30m&#xA;│   └── reserve_free_cache_key.py                               # UUID + threading.Lock&#xA;│&#xA;├── constants/                                                  # constants&#xA;│   ├── pages/&#xA;│   │   ├── homepage.py                                         # HOMEPAGE_URL&#xA;│   │   ├── dashboard.py                                        # DASHBOARD_URL + input ids&#xA;│   │   ├── random_loaders.py&#xA;│   │   ├── sacred_upload.py&#xA;│   │   ├── corsicamon.py&#xA;│   │   ├── chaotic_form.py&#xA;│   │   ├── madness.py&#xA;│   │   ├── random_error_page.py&#xA;│   │   └── donkey_sausage_eater_detector.py&#xA;│   └── sys/&#xA;│       ├── redis_keys.py                                       # OTP_SEND_LOCK_KEY, ...&#xA;│       └── transient_errors.py                                 # (WebDriverException, HttpError...)&#xA;│&#xA;├── lib/&#xA;│   ├── connectors/test_steps/actions/                          # functions (TPOM) -&amp;gt; TPOM&#xA;│   │   ├── homepage.py&#xA;│   │   ├── dashboard_login.py                                  # ~10 connectors (with/without retries)&#xA;│   │   ├── dashboard_welcome.py&#xA;│   │   ├── dashboard_protected_page.py&#xA;│   │   ├── sacred_upload.py&#xA;│   │   ├── corsicamon_enter_api_key.py&#xA;│   │   ├── corsicamon_main.py&#xA;│   │   ├── chaotic_form.py&#xA;│   │   ├── random_error.py&#xA;│   │   ├── random_loaders.py&#xA;│   │   ├── madness.py&#xA;│   │   ├── this_is_bastia.py&#xA;│   │   ├── cors_errors.py&#xA;│   │   ├── bsod.py&#xA;│   │   ├── dsed.py&#xA;│   │   └── ids_bypassed.py&#xA;│   ├── custom_errors/&#xA;│   │   ├── http.py                                             # HttpErrorPageReachedError&#xA;│   │   └── transient_error.py                                  # TransientError&#xA;│   └── ext/                                                    # extensions / adapters&#xA;│       ├── ocarina/&#xA;│       │   ├── adapters/&#xA;│       │   │   ├── agnostic/&#xA;│       │   │   │   ├── act.py                                  # ⭐ act adapter (on_failure hook)&#xA;│       │   │   │   ├── env_getters.py                          # ⭐ typed EnvGetters adapter&#xA;│       │   │   │   └── match_page.py                           # ⭐ match_page adapter&#xA;│       │   │   └── selenium/&#xA;│       │   │       ├── test_suite.py                           # ⭐ TestSuite adapter&#xA;│       │   │       ├── test_campaign.py                        # ⭐ TestCampaign adapter&#xA;│       │   │       ├── cli_getters.py                          # typed getters from CliStore&#xA;│       │   │       ├── logs.py                                 # create_just_log_error, etc.&#xA;│       │   │       └── screenshotter.py                        # take_screenshot helper&#xA;│       │   └── regex/error_page.py                             # ERROR_PAGE_REGEX&#xA;│       ├── redis/&#xA;│       │   └── client.py                                       # Redis Singleton client&#xA;│       └── selenium/&#xA;│           ├── humanize/                                       # ⭐ HumanizedDriver + keyboard&#xA;│           │   ├── proxy.py&#xA;│           │   └── keyboard.py&#xA;│           ├── pages/verify_elements_presence.py&#xA;│           └── watchers/catch_me_if_you_can_watcher.py         # ⭐ Watcher callback&#xA;│&#xA;├── pages/                                                      # POMs (Selenium + SeleniumTitleMixin)&#xA;│   ├── homepage.py&#xA;│   ├── random_loaders.py&#xA;│   ├── chaotic_form.py&#xA;│   ├── random_error.py&#xA;│   ├── sacred_upload/&#xA;│   │   ├── sacred_upload.py&#xA;│   │   └── fixtures/                                           # files to upload&#xA;│   ├── dashboard/&#xA;│   │   ├── login.py                                            # ⭐ with OTP, retries, redis locks&#xA;│   │   ├── welcome_page.py&#xA;│   │   └── protected_page.py&#xA;│   ├── corsicamon/&#xA;│   │   ├── enter_api_key.py&#xA;│   │   └── main.py&#xA;│   ├── madness/&#xA;│   │   ├── base.py&#xA;│   │   ├── matchers.py                                         # has_cors, has_this_is_bastia&#xA;│   │   ├── cors.py&#xA;│   │   └── this_is_bastia.py&#xA;│   └── donkey_sausage_detector/&#xA;│       └── ids_bypassed.py&#xA;│&#xA;└── tests/&#xA;    ├── cycles/e2e.py                                           # ⭐ TestCycle (smoke + main)&#xA;    ├── campaigns/&#xA;    │   ├── global_smoke_tests.py                               # smoke&#xA;    │   ├── corsicamon.py                                       # main (+ smoke variant)&#xA;    │   ├── dashboard_login.py                                  # main&#xA;    │   ├── randomness.py                                       # main&#xA;    │   └── sacred_upload.py                                    # main&#xA;    ├── suites/&#xA;    │   ├── global_smoke_tests.py&#xA;    │   ├── randomness.py&#xA;    │   ├── dashboard/&#xA;    │   │   ├── access/&#xA;    │   │   │   ├── happy_paths.py&#xA;    │   │   │   └── unhappy_paths.py&#xA;    │   │   └── data_driven/multi_login.py&#xA;    │   ├── sacred_upload/{happy_paths,unhappy_paths}.py&#xA;    │   └── corsicamon/{smoke_tests,happy_paths,unhappy_paths}.py&#xA;    └── scenarios/&#xA;        ├── homepage/verify_homepage.py&#xA;        ├── dashboard/&#xA;        │   ├── access/{happy_paths,unhappy_paths}.py&#xA;        │   ├── back_to_igoristan.py&#xA;        │   └── data_driven/{multi_login,datasets/multi_login}.py&#xA;        ├── sacred_upload/{upload_files,just_go_back_to_igoristan}.py&#xA;        ├── corsicamon/{enter_api_key,new_draw,add_corsicamon,back_to_igoristan}.py&#xA;        └── randomness/&#xA;            ├── level_1/{random_error_page,random_loaders_page}.py&#xA;            ├── level_2/{dsed,madness}.py&#xA;            ├── level_3/chaotic_form.py&#xA;            └── level_4/walkthrough.py&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;folder-semantics&#34;&gt;Folder semantics&lt;a class=&#34;anchor&#34; href=&#34;#folder-semantics&#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;Folder&lt;/th&gt;&#xA;          &lt;th&gt;Role&lt;/th&gt;&#xA;          &lt;th&gt;Convention&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;pages/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;POMs (inherit &lt;code&gt;SeleniumTitleMixin, POMBase&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;One class per page, fluent (&lt;code&gt;return self&lt;/code&gt;)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;lib/connectors/test_steps/actions/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Connectors &lt;code&gt;(TPOM) -&amp;gt; TPOM&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Pure functions; if parameters, closures&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;lib/custom_errors/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Project exceptions&lt;/td&gt;&#xA;          &lt;td&gt;Exception subclasses&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;lib/ext/ocarina/adapters/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Adapters above Ocarina&lt;/td&gt;&#xA;          &lt;td&gt;See &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/02-adapters/&#34;&gt;&lt;code&gt;02-adapters.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;lib/ext/redis/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Redis Singleton wrapper&lt;/td&gt;&#xA;          &lt;td&gt;For distributed locks&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;lib/ext/selenium/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Selenium extensions specific to the project&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;HumanizedDriver&lt;/code&gt;, &lt;code&gt;Watcher&lt;/code&gt; callback&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;caches/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;In-memory L1 cache&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;dogpile.cache.memory&lt;/code&gt; + UUID reservation&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;constants/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Constants&lt;/td&gt;&#xA;          &lt;td&gt;URLs, redis keys, transient errors&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;api/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;External HTTP clients&lt;/td&gt;&#xA;          &lt;td&gt;OTP retrieval, OTP history&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;tests/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;ISTQB hierarchy&lt;/td&gt;&#xA;          &lt;td&gt;cycles / campaigns / suites / scenarios&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;lib-vs-pages&#34;&gt;&lt;code&gt;lib/&lt;/code&gt; vs &lt;code&gt;pages/&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#lib-vs-pages&#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;&lt;code&gt;pages/&lt;/code&gt;&lt;/strong&gt;: POMs — encapsulate a page&amp;rsquo;s state (locators, action methods).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;lib/connectors/test_steps/actions/&lt;/code&gt;&lt;/strong&gt;: connectors — functions that call into POMs.&lt;/li&gt;&#xA;&lt;/ol&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;act(on_homepage, open_homepage)&#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;#                ^^^^^^^^^^^^^&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;#                connector defined in lib/connectors/test_steps/actions/homepage.py&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;open_homepage&lt;/span&gt;(p: Homepage) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Homepage:&#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;return&lt;/span&gt; p&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;open()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The AI project&amp;rsquo;s &lt;code&gt;CLAUDE.md&lt;/code&gt; enforces this as a discipline:&lt;/p&gt;</description>
    </item>
    <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>09.01 — VitePress &#43; Sugar Theme &#43; in-house plugins stack</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/01-stack-vitepress/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/01-stack-vitepress/</guid>
      <description>&lt;h1 id=&#34;0901vitepress--sugar-theme--in-house-plugins-stack&#34;&gt;09.01 — VitePress + Sugar Theme + in-house plugins stack&lt;a class=&#34;anchor&#34; href=&#34;#0901vitepress--sugar-theme--in-house-plugins-stack&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;packagejson&#34;&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#packagejson&#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-json&#34; data-lang=&#34;json&#34;&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;&amp;#34;dependencies&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;&amp;#34;@sugarat/theme&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;0.5.17&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:#ff79c6&#34;&gt;&amp;#34;devDependencies&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;&amp;#34;@commitlint/config-conventional&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;20.5.0&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;&amp;#34;@commitlint/cz-commitlint&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;20.5.1&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;&amp;#34;@eslint/js&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;10.0.1&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;&amp;#34;@types/node&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;25.6.0&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;&amp;#34;commitizen&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;4.3.1&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;&amp;#34;editorconfig&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;3.0.2&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;&amp;#34;eslint&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;10.2.1&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;&amp;#34;eslint-plugin-import-x&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;4.16.2&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;&amp;#34;eslint-plugin-perfectionist&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;5.9.0&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;&amp;#34;eslint-plugin-unused-imports&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;4.4.1&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;&amp;#34;globals&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;17.5.0&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;&amp;#34;husky&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;9.1.7&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;&amp;#34;inquirer&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;9.0.0&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;&amp;#34;is-ci&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;4.1.0&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;&amp;#34;lint-staged&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;16.4.0&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;&amp;#34;pagefind&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;1.5.2&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;&amp;#34;prettier&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;3.8.3&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;&amp;#34;sass-embedded&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;1.99.0&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;&amp;#34;typescript&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;6.0.3&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;&amp;#34;typescript-eslint&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;8.59.0&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;&amp;#34;vitepress&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;2.0.0-alpha.17&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;&amp;#34;vitepress-plugin-image-optimize&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;1.5.1&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;&amp;#34;vue&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;3.5.33&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:#ff79c6&#34;&gt;&amp;#34;engines&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;&amp;#34;node&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;^24.x&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;&amp;#34;pnpm&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;11.x&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&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;Lib&lt;/th&gt;&#xA;          &lt;th&gt;Role&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;vitepress 2.0.0-alpha.17&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;SSG framework, Vite-based&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;@sugarat/theme&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;First-class blog theme (templates, auto sidebar, covers)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;vue 3.5.33&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Required by VitePress&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;vitepress-plugin-image-optimize&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Build-time image optimization&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;pagefind 1.5.2&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Static search engine (full-text, side-CDN)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;sass-embedded&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Compiles SCSS (used by &lt;code&gt;@sugarat/theme&lt;/code&gt;)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Husky / lint-staged / commitlint&lt;/td&gt;&#xA;          &lt;td&gt;Same tools as the other repos (Conventional Commits)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;docsvitepressconfigmts&#34;&gt;&lt;code&gt;docs/.vitepress/config.mts&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#docsvitepressconfigmts&#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-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; { defineConfig } &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;vitepress&amp;#39;&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;import&lt;/span&gt; path &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;path&amp;#39;&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;import&lt;/span&gt; fs &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;fs&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;import&lt;/span&gt; { generateSkills } &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;./plugins/skills&amp;#39;&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;import&lt;/span&gt; { generateLlms } &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;./plugins/llm&amp;#39;&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;import&lt;/span&gt; { generatePub } &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;./plugins/pub&amp;#39;&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;import&lt;/span&gt; { blogTheme } &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;./blog-theme&amp;#39;&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;import&lt;/span&gt; webpConfig &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;./plugins/webp&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;const&lt;/span&gt; base &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; process.env.GITHUB_ACTIONS &lt;span style=&#34;color:#ff79c6&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;true&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;/ocarina-holy-book/&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;/&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;export&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;default&lt;/span&gt; defineConfig({&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  locales&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    root&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      themeConfig&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        outline&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; { label&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Table of content&amp;#39;&lt;/span&gt; },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        skipToContentLabel&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Skip to content&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sidebarMenuLabel&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Related articles&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        returnToTopLabel&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Return to top&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        lastUpdatedText&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Last update:&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;      description&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Ocarina is Igor&amp;#39;s automated web browser testing framework.&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      title&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;The Ocarina Holy Book&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      label&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;English&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      lang&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;en&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;    fr&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      themeConfig&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        outline&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; { label&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Sommaire&amp;#39;&lt;/span&gt;, level&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; [&lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;3&lt;/span&gt;] },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        returnToTopLabel&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Retour en haut de page&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        lastUpdatedText&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Dernière mise à jour :&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        skipToContentLabel&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Passer au contenu&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sidebarMenuLabel&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Voir aussi&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;      title&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Le livre sacré d&amp;#39;Ocarina&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      label&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Français&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      lang&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;fr&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;    ru&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      themeConfig&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        outline&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          label&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Оглавление&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          level&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; [&lt;span style=&#34;color:#bd93f9&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;3&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;        skipToContentLabel&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Перейти к содержанию&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        lastUpdatedText&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Последнее обновление:&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        returnToTopLabel&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Вернуться вверх&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sidebarMenuLabel&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Смотрите также&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;      title&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Священная Книга Ocarina&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      label&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Русский&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      lang&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;ru&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;  },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  vite&lt;span style=&#34;color:#ff79c6&#34;&gt;:&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;1-base&#34;&gt;1. &lt;code&gt;base&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#1-base&#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-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt; base &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  process.env.GITHUB_ACTIONS &lt;span style=&#34;color:#ff79c6&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;/ocarina-holy-book/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In CI (GitHub Actions): base = &lt;code&gt;/ocarina-holy-book/&lt;/code&gt; (GitHub Pages prefix).&#xA;Locally: &lt;code&gt;/&lt;/code&gt; (served from the dev server root).&lt;/p&gt;</description>
    </item>
    <item>
      <title>09.03.01 — Review skills</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/01-review/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/01-review/</guid>
      <description>&lt;h1 id=&#34;090301review-skills&#34;&gt;09.03.01 — Review skills&lt;a class=&#34;anchor&#34; href=&#34;#090301review-skills&#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;&lt;strong&gt;Static&lt;/strong&gt; reads, surface findings. A large family. Lets the AI &lt;em&gt;review&lt;/em&gt; its project systematically.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;listing-potentially-non-exhaustive&#34;&gt;Listing (potentially non-exhaustive)&lt;a class=&#34;anchor&#34; href=&#34;#listing-potentially-non-exhaustive&#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;Skill&lt;/th&gt;&#xA;          &lt;th&gt;Target&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;review-spec-gaps&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Clarification questions on the FRDs&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;review-watcher-misuse&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Verifies the &amp;ldquo;&lt;em&gt;negative-only&lt;/em&gt;&amp;rdquo; principle of &lt;code&gt;watcher.report(...)&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;review-compartmentalisation-leaks&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Detects URLs, selectors, magic numbers in wrong places&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;review-dead-code&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Detects unused connectors / POMs / scenarios / suites / fragments / constants&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;review-report&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Classifies each FAIL / SKIP of an execution&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;review-type-ignore&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Audits &lt;code&gt;# type: ignore&lt;/code&gt;s (are they justified?)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;review-match-candidates&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Spots places where a &lt;code&gt;match&lt;/code&gt; (Python, &lt;em&gt;pattern matching&lt;/em&gt;) could be used instead of &lt;code&gt;if&lt;/code&gt; &lt;code&gt;elif&lt;/code&gt; &lt;code&gt;elif&lt;/code&gt; &lt;code&gt;if&lt;/code&gt; &lt;code&gt;if&lt;/code&gt; &lt;code&gt;elif&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;review-unverified-transitions&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Verifies that every &lt;em&gt;page transition&lt;/em&gt; has a &lt;code&gt;verify&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;review-submit-dispatchers&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Audits input-confirmation methods (click vs enter key&amp;hellip;)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;review-comment-drift&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Detects comments that have drifted from the code&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;review-suite-stability&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Evaluates a suite&amp;rsquo;s stability (proportion of retries, transient_errors hits)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;review-intent-collisions&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Detects tests that overwrite each other (conflicting intents) and asks/proposes clarifications&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;review-watcher-emissions&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Audits watcher emissions (volume, dedup, relevance)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;review-hierarchy-naming&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Audits the test-hierarchy naming (&lt;code&gt;TestCycle&lt;/code&gt; / &lt;code&gt;TestCampaign&lt;/code&gt; / &lt;code&gt;TestSuite&lt;/code&gt; / &lt;code&gt;Test&lt;/code&gt;) for the lazy-naming antipattern where a child carries the parent&amp;rsquo;s name&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;review-dead-code&#34;&gt;&lt;code&gt;review-dead-code&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#review-dead-code&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;input  : test base&#xA;output : list of unused elements (connectors / POMs / scenarios / fragments / constants)&#xA;         + per-element recommendation:&#xA;            - delete&#xA;            - move to incubator (&amp;lt;root-source&amp;gt;/incubator/, dependency tree preserved)&#xA;            - keep (justify)&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;review-report&#34;&gt;&lt;code&gt;review-report&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#review-report&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;input  : a recent execution (logs + reports)&#xA;output : per-test classification:&#xA;            - PASS                  (nothing to do)&#xA;            - SKIP                  (why?)&#xA;            - intentional gap FAIL  (G-DATA-*, G-SEC-*, ...)&#xA;            - cross-browser FAIL    (B-BROWSER-*)&#xA;            - transient FAIL        (A-ENV-*)&#xA;            - regression            ⚠️ ALERT&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;review-watcher-misuse&#34;&gt;&lt;code&gt;review-watcher-misuse&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#review-watcher-misuse&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;input  : all watcher.report(...) calls&#xA;output : list of reports that look positive (&amp;#34;success&amp;#34;, &amp;#34;completed&amp;#34;, &amp;#34;ok&amp;#34;, ...)&#xA;         → recommendation: remove or rephrase as negative&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;review-comment-drift&#34;&gt;&lt;code&gt;review-comment-drift&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#review-comment-drift&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;input  : every code comment&#xA;output : list of comments that no longer match the adjacent code&#xA;         (typical: comment mentions foo, code mentions bar)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Helps remove stale comments.&lt;br&gt;&#xA;&amp;ldquo;&lt;em&gt;Teach the pattern, not the symptom&lt;/em&gt;.&amp;rdquo;&lt;/p&gt;</description>
    </item>
    <item>
      <title>10.01 — Workflow summary matrix</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/01-matrix/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/01-matrix/</guid>
      <description>&lt;h1 id=&#34;1001workflow-summary-matrix&#34;&gt;10.01 — Workflow summary matrix&lt;a class=&#34;anchor&#34; href=&#34;#1001workflow-summary-matrix&#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;Synthetic overview of every ecosystem workflow.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;a class=&#34;anchor&#34; href=&#34;#recap&#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;Repo&lt;/th&gt;&#xA;          &lt;th&gt;Workflow&lt;/th&gt;&#xA;          &lt;th&gt;Trigger&lt;/th&gt;&#xA;          &lt;th&gt;OS&lt;/th&gt;&#xA;          &lt;th&gt;Stack&lt;/th&gt;&#xA;          &lt;th&gt;Effect&lt;/th&gt;&#xA;          &lt;th&gt;Detail&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;ocarina&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;main_ci.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;push/PR main, dispatch&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu + windows × py 3.14 (option 3.15-dev)&lt;/td&gt;&#xA;          &lt;td&gt;venv cache, make install-on-ci&lt;/td&gt;&#xA;          &lt;td&gt;make check-coding-style + make test + Allure history + Pages deploy&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/02-ocarina-workflows/&#34;&gt;&lt;code&gt;02-ocarina-workflows.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;dev_ci.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;push dev/feature/fix, PR dev, dispatch&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu × py 3.14&lt;/td&gt;&#xA;          &lt;td&gt;venv cache&lt;/td&gt;&#xA;          &lt;td&gt;check-coding-style + test + coverage summary (non-gating)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/02-ocarina-workflows/&#34;&gt;&lt;code&gt;02-ocarina-workflows.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;unstable_python_full_build.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;cron (monthly), dispatch&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu + windows × py &lt;strong&gt;3.15-dev&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;PIP_ONLY_BINARY=&amp;quot;:all:&amp;quot;&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;full build on pre-release Python&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/02-ocarina-workflows/&#34;&gt;&lt;code&gt;02-ocarina-workflows.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina-example&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;main_ci.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;push/PR main, dispatch&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu + windows × py 3.14&lt;/td&gt;&#xA;          &lt;td&gt;venv cache&lt;/td&gt;&#xA;          &lt;td&gt;check-coding-style&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/03-example-workflows/&#34;&gt;&lt;code&gt;03-example-workflows.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina-example&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;dev_ci.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;push dev, dispatch&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu × py 3.14&lt;/td&gt;&#xA;          &lt;td&gt;venv cache&lt;/td&gt;&#xA;          &lt;td&gt;check-coding-style&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/03-example-workflows/&#34;&gt;&lt;code&gt;03-example-workflows.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina-example&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;e2e.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;dispatch (env &lt;code&gt;OC&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu + service &lt;code&gt;redis:8.x&lt;/code&gt; + Firefox + geckodriver 0.35.0&lt;/td&gt;&#xA;          &lt;td&gt;venv cache&lt;/td&gt;&#xA;          &lt;td&gt;full run, upload traces&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/03-example-workflows/&#34;&gt;&lt;code&gt;03-example-workflows.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina-with-ai-example&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ai_proof_ci.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;push main, PR, dispatch&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu × py 3.14&lt;/td&gt;&#xA;          &lt;td&gt;venv cache&lt;/td&gt;&#xA;          &lt;td&gt;ruff format check + ruff check + mypy&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/04-ai-workflows/&#34;&gt;&lt;code&gt;04-ai-workflows.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina-with-ai-example&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ai_proof_e2e.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;dispatch&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu × &lt;strong&gt;firefox+chrome matrix&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;venv cache, geckodriver 0.36.0 / auto-matched chromedriver&lt;/td&gt;&#xA;          &lt;td&gt;Heroku dyno warm-up, run, sed-filter ChromeDriver stacktraces, upload&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/04-ai-workflows/&#34;&gt;&lt;code&gt;04-ai-workflows.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;igoristan&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ci-pr.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PR &lt;code&gt;**&lt;/code&gt;, dispatch&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu × node 24 + pnpm 11&lt;/td&gt;&#xA;          &lt;td&gt;wireit&lt;/td&gt;&#xA;          &lt;td&gt;format check + lint + typecheck (parallelized)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/05-igoristan-workflows/&#34;&gt;&lt;code&gt;05-igoristan-workflows.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;igoristan&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;deploy.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;push main, dispatch&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu × node 24 + pnpm 11&lt;/td&gt;&#xA;          &lt;td&gt;concurrency group &lt;code&gt;pages&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;pnpm install --frozen-lockfile&lt;/code&gt; + &lt;code&gt;pnpm build&lt;/code&gt; + upload Pages&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/05-igoristan-workflows/&#34;&gt;&lt;code&gt;05-igoristan-workflows.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina-holy-book&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;deploy.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;push main, PR, dispatch&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu × node 24 + pnpm 11&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TZ=Europe/Paris&lt;/code&gt;, concurrency group &lt;code&gt;pages&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;pnpm install&lt;/code&gt; + &lt;code&gt;pnpm build&lt;/code&gt; + upload Pages&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/06-holy-book-workflow/&#34;&gt;&lt;code&gt;06-holy-book-workflow.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;tests-workers&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;none&lt;/em&gt;&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;          &lt;td&gt;Vercel&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;vercel deploy&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/07-tests-workers-vercel/&#34;&gt;&lt;code&gt;07-tests-workers-vercel.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;composite-action&#34;&gt;Composite action&lt;a class=&#34;anchor&#34; href=&#34;#composite-action&#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;Action&lt;/th&gt;&#xA;          &lt;th&gt;Location&lt;/th&gt;&#xA;          &lt;th&gt;Use&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;allure-history&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina/.github/actions/allure-history/action.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Composite: Bun + Allure 3.9.0 + restore/save &lt;code&gt;history.jsonl&lt;/code&gt; on a dedicated branch&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;patterns&#34;&gt;Patterns&lt;a class=&#34;anchor&#34; href=&#34;#patterns&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;pr-workflows-light-auto&#34;&gt;PR workflows (light, auto)&lt;a class=&#34;anchor&#34; href=&#34;#pr-workflows-light-auto&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ocarina/main_ci.yml                                       # python 3.14&#xA;ocarina/dev_ci.yml&#xA;ocarina-example/main_ci.yml&#xA;ocarina-example/dev_ci.yml&#xA;ocarina-with-ai-example/ai_proof_ci.yml&#xA;igoristan/ci-pr.yml&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;All run fast.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.01 — Sovereign grammar</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/11-independence/01-sovereign-grammar/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/11-independence/01-sovereign-grammar/</guid>
      <description>&lt;h1 id=&#34;1101sovereign-grammar&#34;&gt;11.01 — Sovereign grammar&lt;a class=&#34;anchor&#34; href=&#34;#1101sovereign-grammar&#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;No imposed DSL. No plugin ecosystem. Extension by &lt;strong&gt;composition&lt;/strong&gt;, never by inheritance.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;vision&#34;&gt;Vision&lt;a class=&#34;anchor&#34; href=&#34;#vision&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Holy Book quote (chapter &amp;ldquo;&lt;em&gt;What is Ocarina?&lt;/em&gt;&amp;rdquo;):&lt;/p&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;&lt;em&gt;What if delegating one&amp;rsquo;s grammar to &amp;ldquo;standards&amp;rdquo; had never been a good idea?&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;The real gap in E2E testing &lt;strong&gt;is not about imposing the &amp;ldquo;best&amp;rdquo; Newspeak&lt;/strong&gt;.&lt;br&gt;&#xA;Short answer: Ocarina is &lt;strong&gt;extensible&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Any verb and conjunction can be created, all governed by &lt;strong&gt;strict rules that keep the whole thing deeply consistent&lt;/strong&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.01 — Every manifesto quote, sourced</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/01-sourced-citations/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/01-sourced-citations/</guid>
      <description>&lt;h1 id=&#34;1201every-manifesto-quote-sourced&#34;&gt;12.01 — Every manifesto quote, sourced&lt;a class=&#34;anchor&#34; href=&#34;#1201every-manifesto-quote-sourced&#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 Holy Book is packed with quotes whose origin is never announced in the text. They are passwords: if you recognize them, you know where the author comes from; otherwise, you read the slogan without the subtext.&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 style=&#34;text-align: center&#34;&gt;#&lt;/th&gt;&#xA;          &lt;th&gt;Quote (Holy Book)&lt;/th&gt;&#xA;          &lt;th&gt;Author / Source&lt;/th&gt;&#xA;          &lt;th style=&#34;text-align: center&#34;&gt;Year&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;In the Lulzboat, salute, bitch, and show some respect.&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;YTCracker — &lt;code&gt;#antisec&lt;/code&gt;&lt;/strong&gt; (single, official theme song of Operation AntiSec). &lt;a href=&#34;https://open.spotify.com/track/6k1HKAkbIHAimbdpf7Pn5J&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Spotify track&lt;/a&gt;, &lt;a href=&#34;https://en.wikipedia.org/wiki/YTCracker&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;YTCracker Wikipedia page&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;June 22, 2011&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;2&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;THE FUCK YOU THINK THIS IS? You HOLLOW! YOU HOLLOW, YOU UNDERSTAND ME? YOU&amp;rsquo;RE WORTHLESS, YOU&amp;rsquo;RE FUCKING TRASH!&lt;/em&gt;&amp;rdquo;, signed &amp;ldquo;&lt;em&gt;(btw: RIP, DG descendant…)&lt;/em&gt;&amp;rdquo;, then continued in French: &amp;ldquo;&lt;em&gt;ON VA TE REMPLACER ET TU RETOURNERAS HARCELER TES PAIRS SUR DOTA ET TE BRANLER SUR DES MACROS VBA COMME UNE MERDE! […] YOU FUCKED WITH US!&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Yung Innanet (kayos)&lt;/strong&gt; — lyrics from &lt;strong&gt;&lt;code&gt;true colors&lt;/code&gt;&lt;/strong&gt;, &lt;a href=&#34;https://soundcloud.com/queed-inc/true-colors&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;SoundCloud — &lt;em&gt;queed-inc/true-colors&lt;/em&gt;&lt;/a&gt;. Original sysadmin of &lt;a href=&#34;https://en.wikipedia.org/wiki/Vx-underground&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;VX Underground&lt;/a&gt;, &lt;a href=&#34;https://github.com/yunginnanet/yunginnanet&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;GitHub profile&lt;/a&gt;. Passed away on &lt;strong&gt;October 18, 2025&lt;/strong&gt;. The &lt;em&gt;DG descendant&lt;/em&gt; refers to &lt;strong&gt;Digital Gangster&lt;/strong&gt;, YTCracker&amp;rsquo;s forum. See &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/06-yung-innanet-vxug/&#34;&gt;&lt;code&gt;06-yung-innanet-vxug.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;3&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;Of currency minted from factoring digits&lt;/em&gt;&amp;rdquo; (in French in the Holy Book: &amp;ldquo;&lt;em&gt;ces monnaies forgées de la factorisation des nombres&lt;/em&gt;&amp;rdquo;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;YTCracker — &lt;code&gt;Cryptoilluminati&lt;/code&gt;&lt;/strong&gt; (album &lt;em&gt;A Side Quest for Fractional Cents&lt;/em&gt;). &lt;a href=&#34;https://soundcloud.com/ytcracker/ytcracker-cryptoilluminati&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;SoundCloud&lt;/a&gt;. Exact line: &amp;ldquo;&lt;em&gt;Told you to snap up a modest position / Of currency minted from factoring digits / Which of you listened?&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;2022&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;4&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;We Can Do All What You Can&amp;rsquo;t Do.&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Indonesian collective YogyaCarderLink (YCL) mantra&lt;/strong&gt;, one of the earliest carding / defacement groups based in Yogyakarta. See &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/05-indonesian-hackers/&#34;&gt;&lt;code&gt;05-indonesian-hackers.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;early 2000s&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;5&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;I&amp;rsquo;m the bug you can&amp;rsquo;t kill.&lt;/em&gt;&amp;quot; — Holy Book: &amp;ldquo;&lt;em&gt;Je suis le bug dont tu ne pourras jamais te débarrasser.&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Not reliably attributable.&lt;/strong&gt; Idiom of the 2000s hacker scene, in the lineage of &amp;ldquo;&lt;em&gt;I&amp;rsquo;m the null at the end of the string&lt;/em&gt;&amp;rdquo; (YTCracker, &lt;em&gt;Nerdlife&lt;/em&gt;, 2006). See &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/03-ytcracker-nerdcore-digital-gangster/&#34;&gt;&lt;code&gt;03-ytcracker-nerdcore-digital-gangster.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;6&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;HACK THE PLANET&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;Iconic slogan of the 1990s hacker scene. Popular origin: the film &lt;strong&gt;&lt;em&gt;Hackers&lt;/em&gt;&lt;/strong&gt; (1995, Iain Softley), war cry of the character &lt;em&gt;Cereal Killer&lt;/em&gt;. Picked up by the real scene (mailing lists, defaces, conferences).&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;1995 (spread)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;7&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;YOU FUCKED WITH US!&lt;/em&gt;&amp;rdquo; / &amp;ldquo;&lt;em&gt;Don&amp;rsquo;t fuck with us.&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Direct callback to the &lt;code&gt;Don&#39;t fuck with us&lt;/code&gt; mantra&lt;/strong&gt;, recurring signature of &lt;strong&gt;YogyaCarderLink&lt;/strong&gt; deface pages and more broadly of the 2000s carding/defacement scene. The shift from &lt;strong&gt;future/present&lt;/strong&gt; (&amp;quot;&lt;em&gt;don&amp;rsquo;t&lt;/em&gt;&amp;rdquo;, warning) to &lt;strong&gt;past&lt;/strong&gt; (&amp;quot;&lt;em&gt;you fucked&lt;/em&gt;&amp;quot;, finding) is the rhetorical pivot: &lt;em&gt;we warned you, you did it anyway&lt;/em&gt;. See &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/05-indonesian-hackers/&#34;&gt;&lt;code&gt;05-indonesian-hackers.md&lt;/code&gt;&lt;/a&gt; and &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/02-lulzsec-lulzboat-antisec/&#34;&gt;&lt;code&gt;02-lulzsec-lulzboat-antisec.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;2000s&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;8&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;Fucking skids. Fucking normies!&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;Native IRC terms: &lt;strong&gt;&lt;code&gt;skid&lt;/code&gt;&lt;/strong&gt; = abbreviation of &lt;em&gt;script kiddie&lt;/em&gt;; &lt;strong&gt;&lt;code&gt;normie&lt;/code&gt;&lt;/strong&gt; = average / uninitiated user. Not a single attributable quote — it&amp;rsquo;s EFnet / 2600 / Digital Gangster speech. See &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/04-zoneh-irc-efnet/&#34;&gt;&lt;code&gt;04-zoneh-irc-efnet.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;9&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;from Zone-H to a settled life&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Zone-H&lt;/strong&gt; = public deface archive, founded on &lt;strong&gt;March 2, 2002&lt;/strong&gt; by Roberto Preatoni (&lt;em&gt;Sys64738&lt;/em&gt;). See &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/04-zoneh-irc-efnet/&#34;&gt;&lt;code&gt;04-zoneh-irc-efnet.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;2002&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;10&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;CODE IS LAW&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Lawrence Lessig&lt;/strong&gt;, &lt;em&gt;Code and Other Laws of Cyberspace&lt;/em&gt;, MIT 1999. Warning cry on &lt;em&gt;regulation&lt;/em&gt; by code. Repurposed as &lt;em&gt;ideal&lt;/em&gt; by crypto / Ethereum from 2015 onward.&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;1999 / 2015&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;11&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;Also read: Haters, by Paul Graham.&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;Essay by &lt;strong&gt;Paul Graham&lt;/strong&gt; (Y Combinator co-founder), &lt;a href=&#34;https://paulgraham.com/fh.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;paulgraham.com/fh.html&lt;/code&gt;&lt;/a&gt;. On hostile online comments.&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;12&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;I won&amp;rsquo;t let you pay me for my open source — Fuck You&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;David Heinemeier Hansson (DHH)&lt;/strong&gt;, Ruby on Rails creator. &lt;a href=&#34;https://world.hey.com/dhh/i-won-t-let-you-pay-me-for-my-open-source-d7cf4568&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Blog post &lt;em&gt;Hey&lt;/em&gt;&lt;/a&gt;.&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;13&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;Retry flaky blocks.&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Yegor Bugayenko&lt;/strong&gt;, &lt;em&gt;Prompt&lt;/em&gt; (&lt;a href=&#34;https://github.com/yegor256/prompt&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;github.com/yegor256/prompt&lt;/code&gt;&lt;/a&gt;). Author of &lt;em&gt;Angry Tests&lt;/em&gt;.&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;14&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;An idiot admires complexity, a genius admires simplicity…&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Terry Davis&lt;/strong&gt; (1969–2018), TempleOS creator. Considered by some &amp;ldquo;&lt;em&gt;the smartest programmer who ever lived&lt;/em&gt;&amp;rdquo;. Programmer affected by schizophrenia and one of the most iconic victims of cyber-harassment.&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;15&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;To gain knowledge, add things every day. To gain wisdom, remove things every day.&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Lao-Tzu&lt;/strong&gt;, &lt;em&gt;Tao Te Ching&lt;/em&gt;. Alternative for those who don&amp;rsquo;t dare quote Terry A. Davis due to controversy.&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;6th century B.C.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;16&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Antoine de Saint-Exupéry&lt;/strong&gt;, &lt;em&gt;Wind, Sand and Stars&lt;/em&gt; (1939).&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;1939&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;17&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;Style, for both writer and painter, is a matter not of technique but of vision.&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Marcel Proust&lt;/strong&gt; (interview).&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;18&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;For now, live the questions…&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Rainer Maria Rilke&lt;/strong&gt;, &lt;em&gt;Letters to a Young Poet&lt;/em&gt; (1903-1908).&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;1903-1908&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;19&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;Muddy water is best cleared by leaving it alone.&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;Attributed to &lt;strong&gt;Alan Watts&lt;/strong&gt;.&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;20&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;Peace out.&lt;/em&gt;&amp;rdquo; → &lt;strong&gt;YTCracker — &lt;em&gt;Robots Will Definitely Take Your Job&lt;/em&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;YTCracker, &lt;a href=&#34;https://soundcloud.com/ytcracker/ytcracker-robots-will-definitely-take-your-job&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;SoundCloud&lt;/a&gt;. The title &lt;em&gt;is&lt;/em&gt; the thesis: AI will replace incompetent coders. The expression &amp;ldquo;&lt;em&gt;Peace out&lt;/em&gt;&amp;rdquo; is regularly used in this community as a casual / cold farewell.&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;related-reading&#34;&gt;Related reading&lt;a class=&#34;anchor&#34; href=&#34;#related-reading&#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;Hacker scene&lt;/strong&gt; axis (1, 7, 9, 11) → &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/02-lulzsec-lulzboat-antisec/&#34;&gt;&lt;code&gt;02-lulzsec-lulzboat-antisec.md&lt;/code&gt;&lt;/a&gt;, &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/04-zoneh-irc-efnet/&#34;&gt;&lt;code&gt;04-zoneh-irc-efnet.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;YTCracker / Nerdcore&lt;/strong&gt; axis (1, 3, 5, 20) → &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/03-ytcracker-nerdcore-digital-gangster/&#34;&gt;&lt;code&gt;03-ytcracker-nerdcore-digital-gangster.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Indonesian&lt;/strong&gt; axis (4) → &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/05-indonesian-hackers/&#34;&gt;&lt;code&gt;05-indonesian-hackers.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Yung Innanet&lt;/strong&gt; axis (2) → &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/06-yung-innanet-vxug/&#34;&gt;&lt;code&gt;06-yung-innanet-vxug.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Philosophical / wisdom&lt;/strong&gt; axis (14, 15, 16, 17, 18, 19) → already covered in &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/01-philosophy/04-citations-and-influences/&#34;&gt;&lt;code&gt;../01-philosophy/04-citations-and-influences.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Politics of code&lt;/strong&gt; axis (10, 11, 12, 13) → already covered in &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/01-philosophy/05-political-stance/&#34;&gt;&lt;code&gt;../01-philosophy/05-political-stance.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;why-sources-matter&#34;&gt;Why sources matter&lt;a class=&#34;anchor&#34; href=&#34;#why-sources-matter&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;The Holy Book &lt;em&gt;never cites&lt;/em&gt; its sources. Not by negligence, by &lt;strong&gt;scene convention&lt;/strong&gt;: if you recognize, you belong; if you don&amp;rsquo;t, you read the &lt;em&gt;surface&lt;/em&gt; text, which also works. It&amp;rsquo;s a literary device as old as &lt;em&gt;underground&lt;/em&gt; communities. LulzSec did it on their pastebins.&lt;/p&gt;</description>
    </item>
    <item>
      <title>99.01 — Glossary</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/99-references/01-glossary/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/99-references/01-glossary/</guid>
      <description>&lt;h1 id=&#34;9901glossary&#34;&gt;99.01 — Glossary&lt;a class=&#34;anchor&#34; href=&#34;#9901glossary&#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;Terminology used in the Ocarina ecosystem. Original sources in parentheses.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;railway-oriented-programming-rop&#34;&gt;Railway Oriented Programming (ROP)&lt;a class=&#34;anchor&#34; href=&#34;#railway-oriented-programming-rop&#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;Term&lt;/th&gt;&#xA;          &lt;th&gt;Definition&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;ROP&lt;/strong&gt; (Wlaschin, F#)&lt;/td&gt;&#xA;          &lt;td&gt;Functional pattern: success and failure are two parallel &lt;em&gt;rails&lt;/em&gt;; a failure switches the computation to the failure rail, where it stays (short-circuit).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Result[T]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Discriminated union &lt;code&gt;Ok[T] | Fail&lt;/code&gt;. Error carried as &lt;strong&gt;value&lt;/strong&gt;, not raised exception.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Ok[T]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Success wrapper, &lt;code&gt;value: T&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Fail&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Failure wrapper, &lt;code&gt;error: Exception&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;is_ok&lt;/code&gt;, &lt;code&gt;is_fail&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TypeGuard&lt;/code&gt;s that narrow a &lt;code&gt;Result[T]&lt;/code&gt; to &lt;code&gt;Ok[T]&lt;/code&gt; or &lt;code&gt;Fail&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Action[T]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Thunk[Result[T]]&lt;/code&gt; — argumentless function returning a &lt;code&gt;Result[T]&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;ActionStart[T]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Initial state of the ROP action builder. Expects &lt;code&gt;.failure(...)&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;ActionFailure[T]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;After &lt;code&gt;.failure(...)&lt;/code&gt;. Expects &lt;code&gt;.success(...)&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;ActionSuccess[T]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;After &lt;code&gt;.success(...)&lt;/code&gt;. Expects &lt;code&gt;.execute()&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;ActionChain[T]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;After &lt;code&gt;.execute()&lt;/code&gt;. Carries &lt;code&gt;has_failed: bool&lt;/code&gt; and &lt;code&gt;result: Result[T]&lt;/code&gt;. Allows &lt;code&gt;.then(...)&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;NeutralActionStart[T]&lt;/code&gt;, &lt;code&gt;NeutralActionFailure[T]&lt;/code&gt;, &lt;code&gt;NeutralActionSuccess[T]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Inert&lt;/em&gt; states of the failure rail. Stub by no longer doing anything.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;ChainRunner[T]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Lazy encapsulation of an &lt;code&gt;ActionChain&lt;/code&gt;. Executed by &lt;code&gt;.run()&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;drive_page&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Opinionated semantic alias for &lt;code&gt;chain_actions&lt;/code&gt;. &amp;ldquo;&lt;em&gt;Take control of a page&lt;/em&gt;&amp;rdquo;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;match_page&lt;/code&gt; / &lt;code&gt;when&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Conditional branching. First-match wins.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;create_act&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Low-level primitive to create &lt;code&gt;act&lt;/code&gt; (&lt;em&gt;test step&lt;/em&gt;). Wrapped by the user project.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;act&lt;/code&gt;&lt;/strong&gt; (project-side)&lt;/td&gt;&#xA;          &lt;td&gt;User wrapper for &lt;code&gt;create_act&lt;/code&gt; with a project-specific &lt;code&gt;on_failure&lt;/code&gt; hook. Represents a &lt;em&gt;test step&lt;/em&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;istqb--test-professional-vocabulary&#34;&gt;ISTQB / test-professional vocabulary&lt;a class=&#34;anchor&#34; href=&#34;#istqb--test-professional-vocabulary&#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;Term&lt;/th&gt;&#xA;          &lt;th&gt;Definition&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;ISTQB&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;International Software Testing Qualifications Board. Professional body of software testing.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Test cycle&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Top level. A complete execution.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Test campaign&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Level under the cycle. Not directly defined in ISTQB but recognized in tester vocabulary.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Test suite&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Level under the campaign. A collection of &lt;em&gt;executable&lt;/em&gt; tests.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Test case&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;A test case. Atomic unit.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Test step&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;A test step. Atomic action inside a test case.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Smoke test&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Minimal check proving the system is &lt;em&gt;running&lt;/em&gt;. If failure, the rest is useless.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Setup / Teardown&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Pre/post-condition of a test.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;ocarina-orchestration&#34;&gt;Ocarina (orchestration)&lt;a class=&#34;anchor&#34; href=&#34;#ocarina-orchestration&#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;Term&lt;/th&gt;&#xA;          &lt;th&gt;Definition&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;&lt;code&gt;Test[Driver]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;ISTQB test. Metadata + scenario factory + fragments + skip.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;TestSuite[Driver]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;ISTQB suite. Parallelization (&lt;em&gt;thread pool&lt;/em&gt;), saturation, ID filtering, retries.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;TestCampaign[Driver]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Campaign. Sequence of suites with shared workers config.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;TestCycle[Driver]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;ISTQB cycle. Smoke (with &lt;code&gt;fail-fast&lt;/code&gt; vs &lt;code&gt;wait-for-all&lt;/code&gt; modes) + main.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;TestExecutor[Driver]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Executes a single attempt of a test. Stateless.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;TestFlow[Driver]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Replay loop (1 + max_retries) with linear backoff.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;TestRunner[Driver]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Output of &lt;code&gt;Test.spawn(driver, logger)&lt;/code&gt;. Aggregator of chain_runners + setup + teardown + watchers.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Scenario[Driver]&lt;/code&gt;&lt;/strong&gt; (frozen dataclass)&lt;/td&gt;&#xA;          &lt;td&gt;Encapsulates &lt;code&gt;test_chain&lt;/code&gt;, &lt;code&gt;setup&lt;/code&gt;, &lt;code&gt;teardown&lt;/code&gt;, &lt;code&gt;watchers&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;TestChain&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Sequence[ChainRunner[Any]]&lt;/code&gt;. The sequence to execute to apply the test.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Watcher[Driver]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Daemon thread&lt;/em&gt; observing something extra on a test. Periodic callback.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Saturation&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Random test-cloning mechanism to reach &lt;code&gt;max_workers&lt;/code&gt;. Copies named &lt;code&gt;[COPY N] &amp;lt;name&amp;gt;&lt;/code&gt; (or &lt;code&gt;[+N]&lt;/code&gt; per convention).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Transient errors&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Tuple of exceptions triggering a replay.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;max_retries_per_test&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Number of replays. Default, 8 (&amp;quot;&lt;em&gt;9 lives like a cat&lt;/em&gt; 🐱&amp;quot;).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;autoscreen_on_fail&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Automatic screenshot on failing test step.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;ocarina-invariants-dsl&#34;&gt;Ocarina (invariants DSL)&lt;a class=&#34;anchor&#34; href=&#34;#ocarina-invariants-dsl&#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;Term&lt;/th&gt;&#xA;          &lt;th&gt;Definition&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;&lt;code&gt;validate(value, name=&amp;quot;...&amp;quot;)&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Entry point of the invariants DSL.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;ValidationStartBlock[T]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Initial state. Expects &lt;code&gt;.assert_that(...)&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;ValidationAssertBlock[T]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;After &lt;code&gt;.assert_that(...)&lt;/code&gt;. Accepts &lt;code&gt;.assert_that&lt;/code&gt;, &lt;code&gt;.otherwise&lt;/code&gt;, &lt;code&gt;.then&lt;/code&gt;, &lt;code&gt;.execute&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;.assert_that(predicate)&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Adds a logical AND.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;.otherwise(predicate)&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Adds a logical OR. Combined via &lt;code&gt;_any_of&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;.then(new_value)&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Switches the current value in the chain. Keeps accumulated errors.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;.execute()&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Executes the chain, returns a &lt;code&gt;_ValidationResult&lt;/code&gt; (inert until &lt;code&gt;.raise_if_invalid()&lt;/code&gt; is called).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;chain_validations(*)&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Merge several &lt;code&gt;ValidationAssertBlock&lt;/code&gt;s into one.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;InvariantViolationError&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Exception raised by predicates.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;AggregateInvariantViolationError&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Aggregation of multiple &lt;code&gt;InvariantViolationError&lt;/code&gt;s. Raised by &lt;code&gt;.raise_if_invalid()&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;DuplicatesError&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;InvariantViolationError&lt;/code&gt; subclass raised by &lt;code&gt;has_unique_elements&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;BusinessInvariantValidator.create&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Factory for business invariants.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;FrameworkInvariantValidator.create&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Factory for framework invariants.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;ocarina-infra--ports&#34;&gt;Ocarina (infra + ports)&lt;a class=&#34;anchor&#34; href=&#34;#ocarina-infra--ports&#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;Term&lt;/th&gt;&#xA;          &lt;th&gt;Definition&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;&lt;code&gt;POMBase&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Framework-agnostic ABC. Methods &lt;code&gt;verify&lt;/code&gt; + &lt;code&gt;get_current_title&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;SeleniumTitleMixin&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Selenium-specific mixin. Implements &lt;code&gt;get_current_title&lt;/code&gt; and types &lt;code&gt;_driver: WebDriver&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;WebDriversPool[Driver]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Thread-safe drivers pool. Semaphore + Queue (&lt;em&gt;THE QUEUE damn it!&lt;/em&gt;) + warmup with watchdog.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;WarmupTimeoutError&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Raised by &lt;code&gt;pool.warmup()&lt;/code&gt; when it stalls.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;DriverBuilder[Driver]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Builder that manages the profile tmp dir and produces &lt;code&gt;(driver, dispose)&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;BuiltWebDriver[Driver]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;tuple[Driver, Effect]&lt;/code&gt; — driver + dispose.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Screenshotter[TDriver]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Thread-safe screenshot utility. Burst mode, healthcheck.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;ScreenshotterConfig[TDriver]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Config (frozen dataclass): output_dir, file_ext, health_check, save_full_page, etc.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;ActCounter&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Interface. Counts &lt;code&gt;act&lt;/code&gt;s called per attempt.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;ThreadsBasedActCounter&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Thread-local implementation of &lt;code&gt;ActCounter&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;ILogger&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Logging port (ABC). Methods: &lt;code&gt;debug&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warning&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, &lt;code&gt;critical&lt;/code&gt;, &lt;code&gt;success&lt;/code&gt;, &lt;code&gt;test_name&lt;/code&gt;, &lt;code&gt;exception&lt;/code&gt;, &lt;code&gt;raw&lt;/code&gt;, &lt;code&gt;set_prefix&lt;/code&gt;, &lt;code&gt;set_domain_taxonomy&lt;/code&gt;, &lt;code&gt;cleanup&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;ITakeScreenshot[Driver]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Protocol &lt;code&gt;(driver, logger, category) -&amp;gt; None&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;PrintLogger&lt;/code&gt; / &lt;code&gt;FileLogger&lt;/code&gt; / &lt;code&gt;PrintAndFileLogger&lt;/code&gt; / &lt;code&gt;MutedLogger&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;4 canonical implementations of &lt;code&gt;ILogger&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;driver_healthcheck(driver)&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Pings &lt;code&gt;driver.title&lt;/code&gt;; raises &lt;code&gt;DriverDiedError&lt;/code&gt; if THE DRIVER IS DEAD.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;DriverDiedError&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Exception raised by &lt;code&gt;driver_healthcheck&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;PageVerificationError&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Exception raised by &lt;code&gt;POM.verify&lt;/code&gt; when the page isn&amp;rsquo;t the right one.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;NoMatchingBranchError&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Exception raised by &lt;code&gt;match_page&lt;/code&gt; when no &lt;code&gt;when&lt;/code&gt; matches.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;ocarina-opinionated&#34;&gt;Ocarina (opinionated)&lt;a class=&#34;anchor&#34; href=&#34;#ocarina-opinionated&#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;Term&lt;/th&gt;&#xA;          &lt;th&gt;Definition&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;&lt;code&gt;CliBuilder&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Declarative builder over argparse. Error aggregation.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;CliArg&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Declaration of a CLI argument.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;CliStore[TKeys]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Write-once store for parsed CLI values. &lt;code&gt;TKeys: Literal[...]&lt;/code&gt; for autocomplete.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;_CliField[T]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Write-once field with validator.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;phantom_validate&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;No-op validator for fields that don&amp;rsquo;t need extra validation.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;CliStoreSingleton&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Singleton around &lt;code&gt;CliStore&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;create_selenium_auto_cli_store()&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Factory that dispatches by OS.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;bootstrap(test_cycle, run_plugins, post_exec)&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Canonical entry point.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;run_plugins(*plugins, exceptions_logger)&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Runs plugins in parallel if N&amp;gt;1.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;pretty_print_results&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Plugin that prints results to the terminal (ANSI).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;generate_docx_proof&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Plugin to create DOCX test proofs.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;generate_json_results&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Plugin that transcribes the same results as &lt;code&gt;pretty_print_results&lt;/code&gt;, in JSON format.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;timing&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Context manager that measures duration.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;functional-programming&#34;&gt;Functional programming&lt;a class=&#34;anchor&#34; href=&#34;#functional-programming&#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;Term&lt;/th&gt;&#xA;          &lt;th&gt;Definition&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;&lt;code&gt;Effect&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Callable[[], None]&lt;/code&gt; — argumentless, returnless function, deferred.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Thunk[T]&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Callable[[], T]&lt;/code&gt; — argumentless function, deferred.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Closure&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Function capturing an environment.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Fold&lt;/code&gt; / &lt;code&gt;reduce&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Reduction of a sequence to a single value (&lt;em&gt;catamorphism&lt;/em&gt;).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;TypeGuard&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PEP 647 — annotation that &lt;em&gt;narrows&lt;/em&gt; a type on the type-checker side.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Discriminated union&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Union of disjoint types, discriminated by a test (typically &lt;code&gt;isinstance&lt;/code&gt;).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Sealed union&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Discriminated union whose variants are all &lt;code&gt;@final&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;@final&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PEP 591 — declares that a class can&amp;rsquo;t be subclassed (no infinite covariance).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Self&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PEP 673 — return type that refers to the instantiated class (not the declared class).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Protocol&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PEP 544 — structural type; any object with the right shape is usable.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;PEP 695&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Generics syntax &lt;code&gt;class Foo[T]:&lt;/code&gt; (Python 3.12+).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;author-vocabulary&#34;&gt;Author vocabulary&lt;a class=&#34;anchor&#34; href=&#34;#author-vocabulary&#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;Term&lt;/th&gt;&#xA;          &lt;th&gt;Meaning&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;Slipologue&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Neologism. The one pretending &amp;ldquo;&lt;em&gt;to know everything&lt;/em&gt;&amp;rdquo; without practice. Pollutes technical discussions with &lt;em&gt;mental models&lt;/em&gt;. A pain in the ass using ChatGPT or Wikipedia to always have an answer for everything, pretending to speak about a subject he masters when he doesn&amp;rsquo;t.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Vues de l&amp;rsquo;esprit&lt;/strong&gt; (mental models)&lt;/td&gt;&#xA;          &lt;td&gt;Imaginary concept; inapplicable theoretical vision.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;White box&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Auditable, readable code, no hidden logic. Opposite of &lt;em&gt;black box&lt;/em&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Raw data&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Source code, files, simple formats.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Programming astrology&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Esoteric patterns without measurable benefit.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;In the Lulzboat, salute, bitch, and show some respect.&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Quote from &lt;strong&gt;YTCracker — &lt;code&gt;#antisec&lt;/code&gt;&lt;/strong&gt; (2011), official anthem of &lt;em&gt;Operation AntiSec&lt;/em&gt;. See &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/01-sourced-citations/&#34;&gt;&lt;code&gt;../12-manifesto-deciphered/01-sourced-citations.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Fucking skids, fucking normies&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;IRC / EFnet identity marker.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;hacker-scene-chapter-12&#34;&gt;Hacker scene (chapter 12)&lt;a class=&#34;anchor&#34; href=&#34;#hacker-scene-chapter-12&#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;Term&lt;/th&gt;&#xA;          &lt;th&gt;Definition&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;AntiSec movement (1999)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Movement opposed to the cybersecurity industry and &lt;em&gt;full disclosure&lt;/em&gt;. Refusal to publish exploits / vulnerabilities. Targets: SecurityFocus, Packet Storm, Bugtraq mailing list, etc.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Operation AntiSec (2011)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Resurrection of the movement by LulzSec + Anonymous. 50 days of hacks (May-June 2011).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;LulzSec&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Hacker group active May-June 2011. Anonymous &lt;em&gt;splinter&lt;/em&gt;. Manifesto &amp;ldquo;&lt;em&gt;we do it for the lulz&lt;/em&gt;&amp;rdquo;. Arrests in 2012.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;The Lulzboat&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;LulzSec&amp;rsquo;s identity metaphor. Pirate-ship ASCII art signing their communiqués.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Sabu&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Hector Monsegur, LulzSec leader, turned by the FBI in June 2011.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Topiary&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Jake Davis, LulzSec spokesperson.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;YTCracker&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Bryce Case Jr. (born 1982). &lt;em&gt;Nerdcore&lt;/em&gt; rapper, former black hat (NASA Goddard 1999), founder of &lt;em&gt;Digital Gangster&lt;/em&gt; (forum, 2005).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Nerdcore&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Hip-hop subgenre whose lyrics are &lt;em&gt;really&lt;/em&gt; for nerds (code, hacking, sci-fi, math). Coined by MC Frontalot (2000).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Digital Gangster&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Hacker forum founded by YTCracker in 2005 (&lt;code&gt;digitalgangster.com&lt;/code&gt;). 36,000 members at peak. Source of several iconic hacks (Paris Hilton, Miley Cyrus, Twitter Obama, etc.).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;NerdRap Entertainment System&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;YTCracker&amp;rsquo;s founding album (2005). Beats over NES OSTs, Nerdcore lyrics.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Darknet Diaries&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Podcast by &lt;strong&gt;Jack Rhysider&lt;/strong&gt; on cybersecurity history. &lt;strong&gt;Episode 78 (&lt;em&gt;Nerdcore&lt;/em&gt;)&lt;/strong&gt; is dedicated to YTCracker. Transcript: &lt;a href=&#34;https://darknetdiaries.com/transcript/78/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://darknetdiaries.com/transcript/78/&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Zone-H&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Public website-deface archive (founded March 2, 2002 by Roberto Preatoni / Sys64738, based in Estonia). 2002-2015 grey-hat scoreboard.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;IRC&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Internet Relay Chat&lt;/em&gt; (1988). Multi-user text-chat client/server protocol. Central social medium for hackers 1993-2012.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;EFnet&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Eris-Free Network&lt;/em&gt;. Historical IRC network (1990). Center of the &lt;em&gt;warez&lt;/em&gt;, &lt;em&gt;0day&lt;/em&gt;, &lt;em&gt;phreakers&lt;/em&gt; scene 1996-2008.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Channel&lt;/strong&gt; (&lt;code&gt;#name&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;IRC room.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;skid&lt;/code&gt; / &lt;em&gt;script kiddie&lt;/em&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Hacker running scripts they don&amp;rsquo;t understand. Insult.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;normie&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Average user, uninitiated. Insult.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;lamer&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;BBS-era variant of &lt;em&gt;skid&lt;/em&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;gr33tz&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Greetings&lt;/em&gt;, crew list, at the bottom of a deface.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;zine&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;E-zine&lt;/em&gt;, underground magazine, often ASCII.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;VX Underground (VXUG)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Public malware-samples repository (founded ~2019 by smelly_vx). Original sysadmin: &lt;strong&gt;Yung Innanet / kayos&lt;/strong&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Yung Innanet / kayos&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Original sysadmin of VX Underground and musician. Passed October 18, 2025. The Holy Book &lt;strong&gt;directly&lt;/strong&gt; quotes lyrics from his track &lt;strong&gt;&lt;code&gt;true colors&lt;/code&gt;&lt;/strong&gt; (&lt;a href=&#34;https://soundcloud.com/queed-inc/true-colors&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;SoundCloud&lt;/a&gt;), signed &amp;ldquo;&lt;em&gt;(btw: RIP, DG descendant…)&lt;/em&gt;&amp;rdquo;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;YogyaCarderLink (YCL)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Indonesian hacker collective (Yogyakarta), early 2000s. Two recurring mantras on their defaces: &amp;ldquo;&lt;em&gt;We Can Do All What You Can&amp;rsquo;t Do.&lt;/em&gt;&amp;rdquo; and &lt;strong&gt;&lt;code&gt;Don&#39;t fuck with us.&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Honker (红客)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Chinese &lt;em&gt;red hacker&lt;/em&gt;. Patriotic, ideologically driven hacker. Appeared after the 1998 Indonesia riots.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Villains but not Monsters&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Moral distinction at the heart of the hacker scene: a &lt;em&gt;Villain&lt;/em&gt; confronts power, a &lt;em&gt;Monster&lt;/em&gt; persecutes the weak. Real enthusiasts hate &lt;em&gt;monsters&lt;/em&gt; (often former bullied who reproduce the harm they suffered).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;DEF CON&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Annual hacker conference, now in Las Vegas (formerly at the &lt;em&gt;Caesars Forum&lt;/em&gt;).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;BOFH (Bastard Operator From Hell)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Fiction series by &lt;strong&gt;Simon Travaglia&lt;/strong&gt; (New Zealand), published on Usenet in the &amp;rsquo;90s then on &lt;em&gt;The Register&lt;/em&gt;. Archetype of the user-despising sysadmin. Source of the vocabulary &lt;em&gt;PEBKAC&lt;/em&gt;, &lt;em&gt;RTFM&lt;/em&gt;, &lt;em&gt;ID-10-T error&lt;/em&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Usenet&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Distributed &lt;em&gt;newsgroups&lt;/em&gt; system. Founding medium of hacker culture.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Shodan&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Search engine for internet-connected &lt;em&gt;devices&lt;/em&gt; (IP cameras, ICS, IoT, open servers). Founded by &lt;strong&gt;John Matherly&lt;/strong&gt;. &lt;a href=&#34;https://shodan.io&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;shodan.io&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;ViolVocal&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;French-speaking community (mid-2000s) of organized &lt;em&gt;prank callers&lt;/em&gt; / phone harassers on forum + Mumble/TeamSpeak server. SIP spoofing, public call &lt;em&gt;streams&lt;/em&gt;, doxing, impersonating police officers to consult restricted databases without authorization, etc. Shut down after prosecutions. Francophone case of underground cruelty.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;4chan / /b/&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Imageboard founded by moot (Christopher Poole) in 2003. /b/ = no-rules, no-archive, totally anonymous board. Matrix of Anonymous (2006-2008) and modern meme culture. Community regularly orchestrating harassment, having notably degraded Terry A. Davis&amp;rsquo;s health.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Anonymous&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Collective entity emerged from 4chan /b/ around 2006-2008. Op Chanology (Scientology 2008), Op Tunisia (2011), Op Sony (2011). IRC coordination.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Doxing&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Publication of a target&amp;rsquo;s personal info with intent to harm.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Swatting&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;False emergency call (often SIP-spoofed) declaring a serious crime at the victim&amp;rsquo;s address, triggering SWAT deployment. Fatal cases documented.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;OSINT&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Open Source Intelligence&lt;/em&gt;. Information gathering from public sources.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;OPSEC&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Operations Security&lt;/em&gt;. Rigorous maintenance of operational anonymity over time.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Honeypot&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Fake server / fake account / fake leak baiting attackers to detect them. Scene defensive tool.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Snitch / Fed&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Informant / undercover federal agent. Top-priority detection in any &lt;em&gt;underground&lt;/em&gt; community.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Prompt injection&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Insertion of malicious content into an LLM&amp;rsquo;s context to hijack its behavior. OWASP LLM01:2025. First in the LLM Top 10.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;MCP (Model Context Protocol)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Anthropic standard protocol (2024) to expose &lt;em&gt;tools&lt;/em&gt; and &lt;em&gt;resources&lt;/em&gt; to an LLM. Each third-party MCP server adds an attack surface.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Hard-won rules&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Internal project conventions documented in &lt;code&gt;CLAUDE.md&lt;/code&gt; after costly lessons. Prevents LLMs from repeating errors already encountered.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;ocarina-ideology&#34;&gt;Ocarina ideology&lt;a class=&#34;anchor&#34; href=&#34;#ocarina-ideology&#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;Term&lt;/th&gt;&#xA;          &lt;th&gt;Definition&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;Villain (hacker sense)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Reclaimed identity of the sovereign hacker acting outside institutions, opposed to conventions and &lt;em&gt;corporate branding&lt;/em&gt;. The Villain confronts power but respects the weakest.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Monster (hacker sense)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;One who technically exploits their skill to persecute vulnerable people (harasser, doxxer, etc.). Structural &lt;em&gt;enemy&lt;/em&gt; of the real hacker scene.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Tooling culture&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Conviction that well-written technical tools (Nmap, Wireshark, Metasploit, Burp, sqlmap, et al.) made by small teams are worth a thousand marketing presentations.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Tribal loyalty (hacker scene)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Absolute intra-community loyalty + extreme external suspicion. Long admission tests, &lt;em&gt;vouching&lt;/em&gt; by established members, brutal excommunication on betrayal.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Eyes everywhere&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Operational hyper-vigilance learned in the &lt;em&gt;underground&lt;/em&gt; context (IRC admin legally responsible for their channels&amp;rsquo; actions).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;industry--counter-models&#34;&gt;Industry / counter-models&lt;a class=&#34;anchor&#34; href=&#34;#industry--counter-models&#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;Term&lt;/th&gt;&#xA;          &lt;th&gt;Definition&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;Infopreneur&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Entrepreneur whose product is sold information: courses, e-books, masterminds. American model (Dan Kennedy, Russell Brunson), imported into France via Tugan Bara.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Copywriting&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Art of writing persuasive &lt;em&gt;sales texts&lt;/em&gt;. Core trade of infopreneurs. Largely automatable by AI since 2023.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Funnel&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Sales funnel&lt;/em&gt;: free content → front-end product (€97) → flagship product (€997-4997) → premium coaching.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Marketing Underground&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Tugan Bara&amp;rsquo;s flagship program (2018-2023). Owned &amp;ldquo;&lt;em&gt;bad guy&lt;/em&gt;&amp;rdquo; posture.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Tugan Bara / Arnaud Labossière&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;French archetype of the 2018-2024 infopreneur model. HEC dropout. Sold Tugan.ai in 2024. Ultimately the only infopreneur who really explained the profession to a whole francophone-scene community.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;MRR&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Monthly Recurring Revenue&lt;/em&gt;. Monthly amount billed to subscription customers. Central SaaS indicator.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;ARR&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Annual Recurring Revenue&lt;/em&gt;. Annual amount earned from SaaS sales.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;ai-ml-rl&#34;&gt;AI, ML, RL&lt;a class=&#34;anchor&#34; href=&#34;#ai-ml-rl&#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;Term&lt;/th&gt;&#xA;          &lt;th&gt;Definition&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;LLM&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Large Language Model&lt;/em&gt;. Transformer model pre-trained on a large corpus. Claude, GPT, Gemini, Llama, Mistral.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Transformer&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Architecture introduced by &lt;em&gt;Attention Is All You Need&lt;/em&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Foundation model&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Pre-trained model reused for multiple tasks via &lt;em&gt;fine-tuning&lt;/em&gt; or &lt;em&gt;prompting&lt;/em&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;RL (Reinforcement Learning)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Learning by interaction with an environment, guided by a &lt;em&gt;reward signal&lt;/em&gt;. Sutton &amp;amp; Barto 1998.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Constitutional AI (CAI)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Anthropic alignment method (Bai et al., 2022). The model critiques and revises its own responses according to a written &lt;em&gt;constitution&lt;/em&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Probe&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Empirical learning primitive for AI.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;llms.txt, llms-full.txt&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Jeremy Howard standard (fast.ai), 2024. Short Markdown index (&lt;code&gt;llms.txt&lt;/code&gt;) + concatenated full doc (&lt;code&gt;llms-full.txt&lt;/code&gt;) for agent ingestion.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;SKILL.md&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Lets an AI agent plug into a project skill.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;CLAUDE.md&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Provides Claude Code with a set of rules.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;</description>
    </item>
    <item>
      <title>00.02 — Stack / responsibility / license matrix</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/00-big-picture/02-stack-matrix/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/00-big-picture/02-stack-matrix/</guid>
      <description>&lt;h1 id=&#34;0002stack--responsibility--license-matrix&#34;&gt;00.02 — &lt;em&gt;Stack / responsibility / license&lt;/em&gt; matrix&lt;a class=&#34;anchor&#34; href=&#34;#0002stack--responsibility--license-matrix&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Repository&lt;/th&gt;&#xA;          &lt;th&gt;Role&lt;/th&gt;&#xA;          &lt;th&gt;Language&lt;/th&gt;&#xA;          &lt;th style=&#34;text-align: center&#34;&gt;Version&lt;/th&gt;&#xA;          &lt;th&gt;Primary stack&lt;/th&gt;&#xA;          &lt;th&gt;Tooling stack&lt;/th&gt;&#xA;          &lt;th&gt;Distribution&lt;/th&gt;&#xA;          &lt;th style=&#34;text-align: center&#34;&gt;License&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;ocarina&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Browser e2e test framework&lt;/td&gt;&#xA;          &lt;td&gt;Python&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;3.14+&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;hatchling&lt;/code&gt;, &lt;code&gt;python-docx&lt;/code&gt; (sole runtime dependency), &lt;code&gt;selenium&lt;/code&gt; + &lt;code&gt;playwright&lt;/code&gt; (dev only)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ruff&lt;/code&gt; (ALL), &lt;code&gt;mypy&lt;/code&gt; strict, &lt;code&gt;pytest&lt;/code&gt;, &lt;code&gt;hypothesis&lt;/code&gt;, &lt;code&gt;pytest-mypy-plugins&lt;/code&gt;, &lt;code&gt;syrupy&lt;/code&gt;, &lt;code&gt;prysk&lt;/code&gt; (cram), &lt;code&gt;allure-pytest&lt;/code&gt;, &lt;code&gt;pre-commit&lt;/code&gt;, &lt;code&gt;twine&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PyPI, v&lt;strong&gt;1.1.10&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;MIT&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina-example&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Canonical e2e suite against the Igoristan&lt;/td&gt;&#xA;          &lt;td&gt;Python&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;3.14+&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina&lt;/code&gt;, &lt;code&gt;selenium ≥ 4.40&lt;/code&gt;, &lt;code&gt;python-dotenv&lt;/code&gt;, &lt;code&gt;dogpile.cache&lt;/code&gt;, &lt;code&gt;redis&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ruff&lt;/code&gt;, &lt;code&gt;mypy&lt;/code&gt;, &lt;code&gt;pre-commit&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Source only&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;MIT&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina-with-ai-example&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;e2e suite against CURA Healthcare, co-written with Claude Code&lt;/td&gt;&#xA;          &lt;td&gt;Python&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;3.14+&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina ≥ 1.0.3&lt;/code&gt;, &lt;code&gt;selenium ≥ 4.40&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ruff&lt;/code&gt;, &lt;code&gt;mypy&lt;/code&gt;, &lt;code&gt;mypy-extensions&lt;/code&gt;, &lt;code&gt;typing-extensions&lt;/code&gt;, &lt;code&gt;pre-commit&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Source only&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;MIT&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;igoristan&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Public SUT, deliberately chaotic web app&lt;/td&gt;&#xA;          &lt;td&gt;TypeScript&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;6.0.x&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;React 19.2&lt;/strong&gt;, &lt;strong&gt;Vike 0.4.258&lt;/strong&gt; (SSG), &lt;strong&gt;Vite 7.3&lt;/strong&gt;, &lt;strong&gt;TailwindCSS 4&lt;/strong&gt; (alpha), &lt;code&gt;valibot&lt;/code&gt;, &lt;code&gt;react-hook-form&lt;/code&gt;, &lt;code&gt;react-dropzone&lt;/code&gt;, &lt;code&gt;usehooks-ts&lt;/code&gt;, &lt;code&gt;throttleit&lt;/code&gt;, &lt;code&gt;lucide-react&lt;/code&gt;, &lt;code&gt;uuid&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;pnpm 11&lt;/code&gt;, &lt;strong&gt;Node 24&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;wireit&lt;/code&gt;&lt;/strong&gt; (orchestrator), &lt;code&gt;eslint 9&lt;/code&gt; (flat), &lt;code&gt;prettier 3.5&lt;/code&gt;, &lt;code&gt;husky&lt;/code&gt;, &lt;code&gt;lint-staged&lt;/code&gt;, &lt;code&gt;commitlint&lt;/code&gt;, &lt;code&gt;commitizen&lt;/code&gt;, &lt;code&gt;terser&lt;/code&gt;, &lt;code&gt;rollup-plugin-visualizer&lt;/code&gt;, &lt;code&gt;vite-plugin-compression&lt;/code&gt;, &lt;code&gt;@qalisa/vike-plugin-sitemap&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;GitHub Pages (&lt;code&gt;/igoristan/&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;em&gt;none&lt;/em&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;tests-workers&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;OTP / Corsicadex backend, the substrate that exercises &lt;strong&gt;parallelization&lt;/strong&gt; and &lt;strong&gt;API tests&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;TypeScript&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;5.9.3&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Vercel Edge Functions&lt;/strong&gt; (&lt;code&gt;runtime: &amp;quot;edge&amp;quot;&lt;/code&gt;), &lt;code&gt;@upstash/redis&lt;/code&gt;, &lt;code&gt;otplib&lt;/code&gt;, &lt;code&gt;next&lt;/code&gt; types (&lt;code&gt;NextRequest&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;pnpm 11.x&lt;/code&gt;, &lt;code&gt;@vercel/node&lt;/code&gt; (types)&lt;/td&gt;&#xA;          &lt;td&gt;Vercel: &lt;code&gt;https://tests-workers.vercel.app&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;ISC&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina-holy-book&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Public documentation + AI skills + PDFs&lt;/td&gt;&#xA;          &lt;td&gt;TypeScript&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;6.0.x&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;VitePress 2 alpha&lt;/strong&gt;, &lt;code&gt;@sugarat/theme&lt;/code&gt;, &lt;code&gt;vue 3.5&lt;/code&gt;, &lt;code&gt;pagefind&lt;/code&gt;, &lt;code&gt;vitepress-plugin-image-optimize&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;pnpm 11&lt;/code&gt;, Node 24, &lt;code&gt;eslint 9&lt;/code&gt;, &lt;code&gt;prettier 3.8&lt;/code&gt;, &lt;code&gt;husky&lt;/code&gt;, &lt;code&gt;lint-staged&lt;/code&gt;, &lt;code&gt;commitlint&lt;/code&gt;, &lt;code&gt;sass-embedded&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;GitHub Pages (&lt;code&gt;/ocarina-holy-book/&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: center&#34;&gt;MIT&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;python-early-adopter&#34;&gt;Python early adopter&lt;a class=&#34;anchor&#34; href=&#34;#python-early-adopter&#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;Python 3.12+ feature&lt;/th&gt;&#xA;          &lt;th&gt;Use in Ocarina&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;PEP 695&lt;/strong&gt; (&lt;code&gt;class Foo[T]:&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;Generic signatures everywhere (&lt;code&gt;TestSuite[Driver]&lt;/code&gt;, &lt;code&gt;Watcher[Driver]&lt;/code&gt;, &lt;code&gt;Test[Driver]&lt;/code&gt;, &lt;code&gt;Result[T]&lt;/code&gt;, &lt;code&gt;Ok[T]&lt;/code&gt;, &lt;code&gt;Thunk[T]&lt;/code&gt;, etc.)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;PEP 695 type aliases&lt;/strong&gt; (&lt;code&gt;type X = ...&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;Every alias: &lt;code&gt;type Result[T] = Ok[T] | Fail&lt;/code&gt;, &lt;code&gt;type Effect = Callable[[], None]&lt;/code&gt;, &lt;code&gt;type Thunk[T] = Callable[[], T]&lt;/code&gt;, &lt;code&gt;type Mode = Literal[...]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;TypeGuard&lt;/code&gt;&lt;/strong&gt; (Python 3.10+)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_ok&lt;/code&gt;, &lt;code&gt;is_fail&lt;/code&gt;, &lt;code&gt;is_test_result_ok&lt;/code&gt;, &lt;code&gt;is_test_result_fail&lt;/code&gt;, &lt;code&gt;is_test_result_skipped&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Self&lt;/code&gt;&lt;/strong&gt; (3.11+)&lt;/td&gt;&#xA;          &lt;td&gt;Fluent returns from POMs, loggers, invariants&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;@final&lt;/code&gt;&lt;/strong&gt; (3.8+)&lt;/td&gt;&#xA;          &lt;td&gt;Locks critical classes (&lt;code&gt;Watcher&lt;/code&gt;, &lt;code&gt;Test&lt;/code&gt;, &lt;code&gt;TestCycle&lt;/code&gt;, &lt;code&gt;ChainRunner&lt;/code&gt;, &lt;code&gt;Ok&lt;/code&gt;, &lt;code&gt;Fail&lt;/code&gt;, …)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Unpack&lt;/code&gt;&lt;/strong&gt; (3.11+)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;HumanizedDriver&lt;/code&gt; signature in &lt;code&gt;ocarina-example&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Protocol&lt;/code&gt;&lt;/strong&gt; (3.8+)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ScreenshotDriver&lt;/code&gt;, &lt;code&gt;SupportsWrite[str]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Literal&lt;/code&gt;&lt;/strong&gt; (3.8+)&lt;/td&gt;&#xA;          &lt;td&gt;All enums (&lt;code&gt;SeleniumCliStoreKeys&lt;/code&gt;, &lt;code&gt;LOGGERS_CHOICES&lt;/code&gt;, &lt;code&gt;Mode&lt;/code&gt;)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;Never&lt;/code&gt;&lt;/strong&gt; (3.11+)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;_SilentArgumentParser.error&lt;/code&gt; signature&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;&lt;strong&gt;Practical consequence&lt;/strong&gt;: there is a dedicated CI workflow &lt;code&gt;unstable_python_full_build.yml&lt;/code&gt; that pushes Python &lt;strong&gt;3.15-dev&lt;/strong&gt; monthly (&lt;code&gt;cron: &amp;quot;0 3 1 * *&amp;quot;&lt;/code&gt;), because Ocarina aims to be ready for the next interpreter as soon as it ships. See &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/02-ocarina-workflows/&#34;&gt;&lt;code&gt;../10-cicd/02-ocarina-workflows.md&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>01.02 — ISTQB vs pytest / Jest / Mocha</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/01-philosophy/02-istqb-vs-pytest/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/01-philosophy/02-istqb-vs-pytest/</guid>
      <description>&lt;h1 id=&#34;0102istqb-vs-pytest--jest--mocha&#34;&gt;01.02 — ISTQB vs pytest / Jest / Mocha&lt;a class=&#34;anchor&#34; href=&#34;#0102istqb-vs-pytest--jest--mocha&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;the-methodological-observation&#34;&gt;The methodological observation&lt;a class=&#34;anchor&#34; href=&#34;#the-methodological-observation&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Past the technical bet on the technical / non-technical barrier (see &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/01-philosophy/01-flip-the-problem/&#34;&gt;&lt;code&gt;01-flip-the-problem.md&lt;/code&gt;&lt;/a&gt;), there&amp;rsquo;s a &lt;strong&gt;second bet&lt;/strong&gt;: the one on trade vocabulary.&lt;/p&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;The ISTQB and professional testers have spent decades building a precise, battle-tested vocabulary: &lt;em&gt;test cycles&lt;/em&gt;, &lt;em&gt;campaigns&lt;/em&gt;, &lt;em&gt;test suites&lt;/em&gt;, &lt;em&gt;test cases&lt;/em&gt;, &lt;em&gt;test steps&lt;/em&gt;. A clear hierarchy, built to &lt;strong&gt;organize, trace and drive&lt;/strong&gt; software quality.&lt;/p&gt;&#xA;&lt;p&gt;Automation tools have &lt;strong&gt;largely ignored that legacy&lt;/strong&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.02 — The ocarina module tree</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/02-module-tree/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/02-module-tree/</guid>
      <description>&lt;h1 id=&#34;0202the-ocarina-module-tree&#34;&gt;02.02 — The &lt;code&gt;ocarina&lt;/code&gt; module tree&lt;a class=&#34;anchor&#34; href=&#34;#0202the-ocarina-module-tree&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;p&gt;The Python source module ships &lt;strong&gt;134 &lt;code&gt;.py&lt;/code&gt; files&lt;/strong&gt; across four conceptual layers.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;src/ocarina/&#xA;├── __init__.py                              # empty — no global &amp;#34;facade&amp;#34;, everything is imported precisely&#xA;├── py.typed                                 # PEP 561: declares the package as &amp;#34;typed&amp;#34;&#xA;│&#xA;├── railway/                                 # Layer 1 — FP primitives&#xA;│   ├── __init__.py&#xA;│   └── result.py                            # Ok[T] / Fail / Result[T] / is_ok / is_fail&#xA;│&#xA;├── custom_types/                            # Layer 2 — types and aliases&#xA;│   ├── __init__.py&#xA;│   ├── effect.py                            # type Effect = Callable[[], None]&#xA;│   ├── thunk.py                             # type Thunk[T] = Callable[[], T]&#xA;│   ├── tpom.py                              # TypeVar TPOM bound POMBase&#xA;│   ├── supports_write.py                    # Protocol SupportsWrite[T], mirrors a type not exposed by Python stdlib&#xA;│   ├── built_web_driver.py                  # type BuiltWebDriver[Driver] = tuple[Driver, Effect]&#xA;│   ├── scenario.py                          # Scenario[Driver] (frozen dataclass)&#xA;│   ├── test_components.py                   # TestChain / TestSetup / TestTeardown / TestWatchers&#xA;│   ├── test_runner.py                       # TestRunner[Driver]&#xA;│   ├── oc_test.py                           # TestName / TestScenario / TestScenarioFragment&#xA;│   ├── oc_test_layers.py                    # TestResult / TestSuiteResult / TestCampaignResults / TestCycleResults&#xA;│   ├── selenium/&#xA;│   │   ├── built_web_driver.py              # BuiltSeleniumWebDriver&#xA;│   │   ├── oc_test_scenario.py&#xA;│   │   ├── supported_browsers.py            # type SupportedSeleniumBrowser = Literal[&amp;#34;chrome&amp;#34;, &amp;#34;firefox&amp;#34;, &amp;#34;edge&amp;#34;, &amp;#34;safari&amp;#34;]&#xA;│   │   └── web_drivers_pool.py              # type SeleniumWebDriversPool = WebDriversPool[WebDriver]&#xA;│   └── playwright/&#xA;│       ├── built_web_driver.py              # type BuiltPlaywrightDriver = BuiltWebDriver[PlaywrightDriver]&#xA;│       ├── oc_test_scenario.py              # PlaywrightTestScenario / PlaywrightTestScenarioFragment&#xA;│       ├── supported_browsers.py            # type SupportedPlaywrightBrowser = Literal[&amp;#34;chromium&amp;#34;, &amp;#34;firefox&amp;#34;, &amp;#34;webkit&amp;#34;]&#xA;│       └── web_drivers_pool.py              # type PlaywrightDriversPool = WebDriversPool[PlaywrightDriver]&#xA;│&#xA;├── custom_errors/                           # Layer 2: errors&#xA;│   ├── __init__.py&#xA;│   └── test_framework/&#xA;│       ├── __init__.py&#xA;│       ├── no_matching_branch.py            # NoMatchingBranchError&#xA;│       ├── pages.py                         # PageVerificationError&#xA;│       ├── driver_died.py                   # DriverDiedError&#xA;│       └── campaigns.py&#xA;│&#xA;├── custom_invariants/                       # Layer 2: ready-to-use invariants&#xA;│   ├── __init__.py&#xA;│   └── testing/&#xA;│       ├── __init__.py&#xA;│       ├── workers.py                       # validate_workers_amount&#xA;│       ├── oc_test_runners_ids.py           # validate_test_runners_ids&#xA;│       ├── oc_test_runners_names.py         # validate_test_runners_names (unique + valid filename)&#xA;│       ├── oc_test_suites_names.py&#xA;│       ├── oc_test_campaigns_names.py&#xA;│       └── oc_test_cycles_names.py&#xA;│&#xA;├── ports/                                   # Layer 3: ports (interfaces)&#xA;│   ├── __init__.py&#xA;│   ├── ilogger.py                           # ILogger (ABC)&#xA;│   └── itake_screenshot.py                  # ITakeScreenshot[Driver] (Protocol)&#xA;│&#xA;├── pom/                                     # Layer 3: Page Object Model&#xA;│   ├── __init__.py&#xA;│   ├── base.py                              # POMBase (ABC: verify + get_current_title)&#xA;│   ├── selenium/&#xA;│   │   ├── __init__.py&#xA;│   │   └── muted.py                         # MutedPOM utility&#xA;│   └── playwright/&#xA;│       ├── __init__.py&#xA;│       └── muted.py                         # MutedPlaywrightPOM utility&#xA;│&#xA;├── aggregates/                              # Layer 3: result aggregates&#xA;│   ├── __init__.py&#xA;│   └── tests_layers.py                      # is_test_result_ok / _fail / _skipped (TypeGuard)&#xA;│&#xA;├── dsl/                                     # Layer 4: Ocarina DSL&#xA;│   ├── __init__.py&#xA;│   ├── invariants/&#xA;│   │   ├── __init__.py&#xA;│   │   ├── validate.py                      # entry-point validate(), BusinessInvariantValidator, FrameworkInvariantValidator&#xA;│   │   ├── assertions.py                    # is_str / is_email / is_positive / is_in / has_unique_elements / each / ...&#xA;│   │   ├── errors.py                        # InvariantViolationError / DuplicatesError / AggregateInvariantViolationError&#xA;│   │   └── internals/&#xA;│   │       ├── __init__.py&#xA;│   │       └── validation_chain.py          # ValidationStartBlock / ValidationAssertBlock / _ValidationChain / _any_of / chain_validations&#xA;│   ├── testing/&#xA;│   │   ├── __init__.py&#xA;│   │   ├── oc_test.py                       # class Test[Driver]&#xA;│   │   ├── oc_test_suite.py                 # class TestSuite[Driver]&#xA;│   │   ├── oc_test_campaign.py              # class TestCampaign[Driver] + campaign_has_failed&#xA;│   │   ├── oc_test_cycle.py                 # class TestCycle[Driver] + type Mode + has_test_cycle_failed&#xA;│   │   ├── filter_tests_by_ids.py&#xA;│   │   ├── watcher.py                       # class Watcher[Driver]&#xA;│   │   ├── internals/&#xA;│   │   │   ├── __init__.py&#xA;│   │   │   ├── test_executor.py             # class TestExecutor[Driver] + ExecutionOutcome (frozen, slots)&#xA;│   │   │   └── test_flow.py                 # class TestFlow[Driver]&#xA;│   │   ├── selenium/&#xA;│   │   │   ├── __init__.py&#xA;│   │   │   ├── create_test.py               # create_selenium_test(...)&#xA;│   │   │   └── create_watcher.py            # create_selenium_watcher(...) + type SeleniumWatcher&#xA;│   │   └── playwright/&#xA;│   │       ├── __init__.py&#xA;│   │       ├── create_test.py               # create_playwright_test(...)&#xA;│   │       └── create_watcher.py            # create_playwright_watcher(...) + type PlaywrightWatcher&#xA;│   └── testing_with_railway/&#xA;│       ├── __init__.py&#xA;│       ├── chain_actions.py                 # class ChainRunner[T] + chain_actions&#xA;│       ├── match_page.py                    # When + _match_page_builder + create_match_page&#xA;│       ├── constructors/&#xA;│       │   ├── __init__.py&#xA;│       │   └── create_act.py                # low-level primitive (to be wrapped on the project side)&#xA;│       └── internals/&#xA;│           ├── __init__.py&#xA;│           └── action_chain.py              # ActionStart → ActionFailure → ActionSuccess → ActionChain&#xA;│                                            # + Neutral{Start,Failure,Success}  (failure rail)&#xA;│&#xA;├── infra/                                   # Layer 5: infrastructure&#xA;│   ├── __init__.py&#xA;│   ├── drivers_pool.py                      # class WebDriversPool[Driver] + WarmupTimeoutError&#xA;│   ├── driver_builder.py                    # class DriverBuilder[Driver]&#xA;│   ├── screenshotter.py                     # class Screenshotter[TDriver] + ScreenshotterConfig&#xA;│   ├── act_counter.py                       # class ActCounter (interface)&#xA;│   ├── selenium/&#xA;│   │   ├── __init__.py&#xA;│   │   ├── create_driver.py                 # _build_firefox / _build_chrome / _build_edge / _build_safari&#xA;│   │   ├── create_drivers_pool.py           # create_selenium_drivers_pool&#xA;│   │   ├── create_screenshotter.py          # create_selenium_screenshotter&#xA;│   │   ├── driver_healthcheck.py            # driver_healthcheck (pings driver.title)&#xA;│   │   └── mixins.py                        # SeleniumTitleMixin (typing keyway)&#xA;│   └── playwright/&#xA;│       ├── __init__.py&#xA;│       ├── create_driver.py                 # create_playwright_driver (chromium / firefox / webkit)&#xA;│       ├── create_drivers_pool.py           # create_playwright_drivers_pool&#xA;│       ├── create_screenshotter.py          # create_playwright_screenshotter + _playwright_save_full_page&#xA;│       ├── driver.py                        # class PlaywrightDriver (sync wrapper)&#xA;│       ├── driver_healthcheck.py            # playwright_driver_healthcheck (pings driver.title)&#xA;│       └── mixins.py                        # PlaywrightTitleMixin (typing keyway)&#xA;│&#xA;└── opinionated/                             # Layer 6: opt-in, everything that is &amp;#34;nice but replaceable&amp;#34;&#xA;    ├── __init__.py&#xA;    ├── consts/loggers_choices.py            # LOGGERS_CHOICES = (&amp;#34;terminal&amp;#34;, &amp;#34;file&amp;#34;, &amp;#34;terminal+file&amp;#34;, &amp;#34;muted&amp;#34;)&#xA;    ├── infra/&#xA;    │   ├── __init__.py&#xA;    │   └── act_counter.py                   # ThreadsBasedActCounter (thread-local counter)&#xA;    ├── cli/&#xA;    │   ├── __init__.py&#xA;    │   ├── builder.py                       # CliBuilder + CliArg + _SilentArgumentParser&#xA;    │   ├── store.py                         # CliStore[TKeys] + _CliField[T] + field(...)&#xA;    │   ├── phantoms.py                      # phantom_validate (no-op predicate)&#xA;    │   ├── selenium/&#xA;    │   │   ├── __init__.py&#xA;    │   │   ├── cli_store_singleton.py       # SeleniumCliStoreSingleton (push / get)&#xA;    │   │   └── create_cli_store.py          # create_selenium_{auto,win,macos,linux}_cli_store&#xA;    │   └── playwright/&#xA;    │       ├── __init__.py&#xA;    │       ├── cli_store_singleton.py       # PlaywrightCliStoreSingleton (push / get)&#xA;    │       └── create_cli_store.py          # create_playwright_{,auto_}cli_store&#xA;    ├── dsl/&#xA;    │   ├── __init__.py&#xA;    │   └── drive_page.py                    # drive_page = chain_actions, a semantic alias&#xA;    ├── launcher/&#xA;    │   ├── __init__.py&#xA;    │   └── bootstrap.py                     # bootstrap(...) + run_plugins(*plugins, exceptions_logger)&#xA;    ├── loggers/&#xA;    │   ├── __init__.py&#xA;    │   ├── create_matching_logger.py        # create_matching_logger(&amp;#34;terminal&amp;#34;|&amp;#34;file&amp;#34;|...) + get_default_log_dir&#xA;    │   ├── print_logger.py                  # PrintLogger (ANSI)&#xA;    │   ├── file_logger.py                   # FileLogger (taxonomy -&amp;gt; file tree)&#xA;    │   ├── print_and_file_logger.py&#xA;    │   ├── muted_logger.py&#xA;    │   ├── custom_types/supported_loggers.py&#xA;    │   └── utils/&#xA;    │       ├── __init__.py&#xA;    │       └── format_metadata_str.py       # format_utc_date_metadata_str / format_current_thread_metadata_str / concat_metadata&#xA;    └── plugins/&#xA;        ├── __init__.py&#xA;        └── reports/&#xA;            ├── __init__.py&#xA;            ├── pretty_print_results.py      # hierarchical ANSI report&#xA;            ├── results_to_json.py           # JSON generator&#xA;            ├── docx_tests_proofs.py         # DOCX generator (consumes the log tree)&#xA;            └── timing.py                    # context manager `with timing(prefix=&amp;#34;Duration:&amp;#34;)`&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;layered-diagram&#34;&gt;Layered diagram&lt;a class=&#34;anchor&#34; href=&#34;#layered-diagram&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;┌──────────────────────────────────────────────────────────────────────────┐&#xA;│ USER PROJECT (e.g. ocarina-example, ocarina-with-ai-example)             │&#xA;│  ┌────────────────────┐  ┌────────────────────┐  ┌─────────────────────┐ │&#xA;│  │ pages/ (POMBase)   │  │ scenarios/ (Test)  │  │ adapters/ (act, ...)│ │&#xA;│  └────────────────────┘  └────────────────────┘  └─────────────────────┘ │&#xA;└─────────────────────────────────┬────────────────────────────────────────┘&#xA;                                  │ imports&#xA;┌─────────────────────────────────▼────────────────────────────────────────┐&#xA;│ LAYER 6 — opinionated/  (CLI, loggers, plugins, bootstrap)               │&#xA;│   Opt-in. Can be replaced wholesale.                                     │&#xA;└─────────────────────────────────┬────────────────────────────────────────┘&#xA;                                  │&#xA;┌─────────────────────────────────▼────────────────────────────────────────┐&#xA;│ LAYER 5 — infra/  (pool, builder, screenshotter, Selenium adapters)      │&#xA;│   I/O. No DSL logic.                                                     │&#xA;└─────────────────────────────────┬────────────────────────────────────────┘&#xA;                                  │&#xA;┌─────────────────────────────────▼────────────────────────────────────────┐&#xA;│ LAYER 4 — dsl/  (testing, invariants, testing_with_railway)              │&#xA;│   Pure DSL, no I/O. All the grammar.                                     │&#xA;└─────────────────────────────────┬────────────────────────────────────────┘&#xA;                                  │&#xA;┌─────────────────────────────────▼────────────────────────────────────────┐&#xA;│ LAYER 3 — ports/  +  pom/  +  aggregates/                                │&#xA;│   Interfaces and abstractions.                                           │&#xA;└─────────────────────────────────┬────────────────────────────────────────┘&#xA;                                  │&#xA;┌─────────────────────────────────▼────────────────────────────────────────┐&#xA;│ LAYER 2 — custom_types/  +  custom_errors/  +  custom_invariants/        │&#xA;│   Shapes, aliases, types, exceptions, ready-to-use invariants.           │&#xA;└─────────────────────────────────┬────────────────────────────────────────┘&#xA;                                  │&#xA;┌─────────────────────────────────▼────────────────────────────────────────┐&#xA;│ LAYER 1 — railway/  (Result, Ok, Fail, is_ok, is_fail)                   │&#xA;│   The functional primitive. No dependencies.                             │&#xA;└──────────────────────────────────────────────────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This &lt;strong&gt;strict stratification&lt;/strong&gt; rules out &lt;em&gt;import cycles&lt;/em&gt; and lets you swap any upper layer without touching the ones below:&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.03.02 — The builder state machine</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/03-railway/02-action-chain-states/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/03-railway/02-action-chain-states/</guid>
      <description>&lt;h1 id=&#34;020302the-builder-state-machine&#34;&gt;02.03.02 — The builder state machine&lt;a class=&#34;anchor&#34; href=&#34;#020302the-builder-state-machine&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing_with_railway/internals/action_chain.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing_with_railway/internals/action_chain.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s where the &lt;em&gt;entire&lt;/em&gt; ROP mechanic lives: four successive typed states that literally (as in: the &lt;em&gt;type-checker&lt;/em&gt; literally) refuse any syntactic improvisation. The DSL doubles as its own immune system.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;a class=&#34;anchor&#34; href=&#34;#overview&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;                      ┌─────────────────┐&#xA;   start(action)  →   │  ActionStart[T] │     an Action[T] = Thunk[Result[T]]&#xA;                      └────────┬────────┘&#xA;                               │ .failure(failure_handler)&#xA;                               ▼&#xA;                      ┌─────────────────┐&#xA;                      │ ActionFailure[T]│&#xA;                      └────────┬────────┘&#xA;                               │ .success(success_handler)&#xA;                               ▼&#xA;                      ┌─────────────────┐&#xA;                      │ ActionSuccess[T]│&#xA;                      └────────┬────────┘&#xA;                               │ .execute()   ── runs action(), fires the handler&#xA;                               ▼&#xA;                      ┌─────────────────┐&#xA;                      │ ActionChain[T]  │     holds (has_failed, result)&#xA;                      └──────┬──────────┘&#xA;                             │ .then(next_action_or_start)&#xA;              ┌──────────────┴──────────────┐&#xA;              │                             │&#xA;   has_failed=True                   has_failed=False&#xA;              │                             │&#xA;              ▼                             ▼&#xA;   ┌──────────────────┐         ┌───────────────────┐&#xA;   │ NeutralAction-   │         │ ActionStart[T]    │  → cycle starts over&#xA;   │ Start[T]         │         │  (the next action │&#xA;   │ (failure rail:   │         │   will execute)   │&#xA;   │  the whole chain │         └───────────────────┘&#xA;   │  becomes no-op,  │&#xA;   │  while keeping   │&#xA;   │  the fluent API) │&#xA;   └──────────────────┘&#xA;              │&#xA;              ▼ (.failure → .success → .execute, all no-op)&#xA;   ┌──────────────────┐&#xA;   │  ActionChain[T]  │   has_failed=True, result=&amp;lt;accumulated Fail&amp;gt;&#xA;   └──────────────────┘&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;the-types-action-failurehandler-succhandler&#34;&gt;The types &lt;code&gt;Action&lt;/code&gt;, &lt;code&gt;FailureHandler&lt;/code&gt;, &lt;code&gt;SuccHandler&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#the-types-action-failurehandler-succhandler&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Action[T]        &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Thunk[Result[T]]      &lt;span style=&#34;color:#6272a4&#34;&gt;# () -&amp;gt; Result[T]&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:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; FailureHandler   &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Callable[[Exception], &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; SuccHandler      &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Effect                &lt;span style=&#34;color:#6272a4&#34;&gt;# () -&amp;gt; None&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;Action[T]&lt;/code&gt; is a &lt;code&gt;Thunk&lt;/code&gt;.&lt;/strong&gt; Calling &lt;code&gt;ActionStart(action)&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; run anything. The action just gets captured.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;FailureHandler&lt;/code&gt; gets the exception&lt;/strong&gt;, not the &lt;code&gt;Fail&lt;/code&gt; or the &lt;code&gt;Result&lt;/code&gt;. Deliberate: 99% of handlers want to log the exception, take a screenshot, and move on.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;SuccHandler&lt;/code&gt; is an &lt;code&gt;Effect&lt;/code&gt;&lt;/strong&gt; (no argument). Same idea — a success handler logs a static message, snaps a screenshot, doesn&amp;rsquo;t need the result.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;typestate-pattern-why-you-cant-get-it-wrong&#34;&gt;Typestate Pattern: why you can&amp;rsquo;t get it wrong&lt;a class=&#34;anchor&#34; href=&#34;#typestate-pattern-why-you-cant-get-it-wrong&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;The chain is &lt;strong&gt;strictly linear&lt;/strong&gt;. Each method returns a different type that only exposes the &lt;em&gt;next legal step&lt;/em&gt;:&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.04.02 — Catalog of builtin assertions</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/04-invariants/02-assertions/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/04-invariants/02-assertions/</guid>
      <description>&lt;h1 id=&#34;020402catalog-of-builtin-assertions&#34;&gt;02.04.02 — Catalog of builtin assertions&lt;a class=&#34;anchor&#34; href=&#34;#020402catalog-of-builtin-assertions&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/invariants/assertions.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/invariants/assertions.py&lt;/code&gt;&lt;/a&gt; — ~25 predicates.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;p&gt;Every assertion follows the &lt;strong&gt;same contract&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;A direct predicate &lt;code&gt;(value: T) -&amp;gt; None&lt;/code&gt; that raises &lt;code&gt;InvariantViolationError&lt;/code&gt; on contract violation.&lt;/li&gt;&#xA;&lt;li&gt;Or a &lt;strong&gt;closure&lt;/strong&gt; when you need a config argument (&lt;code&gt;is_equal_to(cmp)&lt;/code&gt;, etc.).&lt;/li&gt;&#xA;&lt;li&gt;Or, occasionally, a &lt;strong&gt;higher-order function (HOF)&lt;/strong&gt; like &lt;code&gt;each&lt;/code&gt;. Lives here pragmatically — a dedicated file for a single case would be overkill.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;full-table&#34;&gt;Full table&lt;a class=&#34;anchor&#34; href=&#34;#full-table&#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;Assertion&lt;/th&gt;&#xA;          &lt;th&gt;Direct / closure / HOF&lt;/th&gt;&#xA;          &lt;th&gt;Domain&lt;/th&gt;&#xA;          &lt;th&gt;Description&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;is_str(value)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;direct&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Any&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Raises if &lt;code&gt;value&lt;/code&gt; is not a &lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_none(value)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;direct&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Any&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Raises if &lt;code&gt;value is not None&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_not_none(value)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;direct&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Any&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Raises if &lt;code&gt;value is None&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_equal_to(cmp)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;closure&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Any&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Returns &lt;code&gt;(value) -&amp;gt; None&lt;/code&gt; that raises if &lt;code&gt;value != cmp&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_not_equal_to(cmp)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;closure&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Any&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Returns &lt;code&gt;(value) -&amp;gt; None&lt;/code&gt; that raises if &lt;code&gt;value == cmp&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_less_than(cmp)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;closure&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;float&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;value &amp;lt; cmp&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_less_than_or_equal_to(cmp)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;closure&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;float&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;value &amp;lt;= cmp&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_greater_than(cmp)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;closure&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;float&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;value &amp;gt; cmp&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_greater_than_or_equal_to(cmp)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;closure&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;float&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;value &amp;gt;= cmp&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_positive(value)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;direct&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;float&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;value &amp;gt;= 0&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_not_zero(value)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;direct&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;float&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;value != 0&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_in(elements)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;closure&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Any&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;value in tuple(elements)&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_file(value)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;direct&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str | Path&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Path(value).is_file()&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_dir(value)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;direct&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str | Path&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Path(value).is_dir()&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_iso_date_string(value)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;direct&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;datetime.fromisoformat(value)&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_iso_utc_date_string(value)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;direct&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Checks ISO + &lt;code&gt;tzinfo == UTC&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_email(value)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;direct&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;No whitespace, exactly one &lt;code&gt;@&lt;/code&gt;, non-empty parts, domain contains &lt;code&gt;.&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;has_unique_elements(*, key=None)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;closure&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Iterable[Any]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Detects duplicates (raises &lt;code&gt;DuplicatesError&lt;/code&gt;). Type-strict (&lt;code&gt;1 ≠ True&lt;/code&gt;), supports unhashables.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_empty(value)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;direct&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Sized&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;len(value) == 0&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_truthy(value)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;direct&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Any&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool(value) is True&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_valid_filename(value)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;direct&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Cross-platform: forbidden chars, Windows reserved words, no leading/trailing space or dot, length ≤ 255&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;each(predicate)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;HOF&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Iterable[Any]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Applies &lt;code&gt;predicate&lt;/code&gt; to each element&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;a-few-implementations-in-detail&#34;&gt;A few implementations in detail&lt;a class=&#34;anchor&#34; href=&#34;#a-few-implementations-in-detail&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;is_email&#34;&gt;&lt;code&gt;is_email&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#is_email&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;is_email&lt;/span&gt;(value: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;&amp;#34;&amp;#34;Assert that the string is a valid email address (fast check).&amp;#34;&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:#ff79c6&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; value:&#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;raise&lt;/span&gt; InvariantViolationError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;value&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39; must not contain whitespace.&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;if&lt;/span&gt; value&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;count(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;@&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&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;raise&lt;/span&gt; InvariantViolationError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;value&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39; must contain exactly one &amp;#39;@&amp;#39; character.&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    local_part, domain_part &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; value&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;split(&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:#ff79c6&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; local_part &lt;span style=&#34;color:#ff79c6&#34;&gt;or&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; domain_part:&#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;raise&lt;/span&gt; InvariantViolationError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;value&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39; must have non-empty local and domain parts.&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;if&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; domain_part:&#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;raise&lt;/span&gt; InvariantViolationError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Domain part of &amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;value&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39; must contain at least one &amp;#39;.&amp;#39;.&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Four bare-minimum checks. No RFC 5322 regex. Deliberate: real email validation happens by &lt;em&gt;sending&lt;/em&gt; an email, not by regex.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.05.02 — TestExecutor[Driver]</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/05-orchestration/02-test-executor/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/05-orchestration/02-test-executor/</guid>
      <description>&lt;h1 id=&#34;020502testexecutordriver&#34;&gt;02.05.02 — &lt;code&gt;TestExecutor[Driver]&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#020502testexecutordriver&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/internals/test_executor.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing/internals/test_executor.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;&lt;strong&gt;One job&lt;/strong&gt;: run &lt;strong&gt;one attempt&lt;/strong&gt; of a test with &lt;strong&gt;one driver&lt;/strong&gt;. Knows nothing about retries, the driver pool, or suite-level aggregation.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;executionoutcome&#34;&gt;&lt;code&gt;ExecutionOutcome&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#executionoutcome&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@final&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@dataclass(frozen&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;, slots&lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;ExecutionOutcome&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    result: TestResult&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    skipped: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    setup_failed: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    should_retry: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    steps_count: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;&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;Field&lt;/th&gt;&#xA;          &lt;th&gt;Type&lt;/th&gt;&#xA;          &lt;th&gt;Meaning&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;result&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestResult&lt;/code&gt; = &lt;code&gt;Result[Any] | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;The chain&amp;rsquo;s result (Ok, Fail), or &lt;code&gt;None&lt;/code&gt; if skip / setup_failed.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;skipped&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;True if &lt;code&gt;test_runner.skipped&lt;/code&gt; (i.e. &lt;code&gt;Test(skipped=True)&lt;/code&gt;).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;setup_failed&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;True if the scenario&amp;rsquo;s &lt;code&gt;setup()&lt;/code&gt; raised.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;should_retry&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;True if the &lt;em&gt;retry rule&lt;/em&gt; applies (transient_error detected).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;steps_count&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Number of &lt;code&gt;act&lt;/code&gt; calls; &lt;code&gt;-1&lt;/code&gt; if skip or setup_failed.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;slots=True&lt;/code&gt;&lt;/strong&gt;: blocks dynamic attribute addition, saves memory. This object rides the hot path — &lt;code&gt;slots&lt;/code&gt; earns its keep.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;frozen=True&lt;/code&gt;&lt;/strong&gt;: immutable, safe to share across threads.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;@final&lt;/code&gt;&lt;/strong&gt;: no inheritance.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;execution-order-for-one-attempt&#34;&gt;Execution order for one attempt&lt;a class=&#34;anchor&#34; href=&#34;#execution-order-for-one-attempt&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;┌──────────────────────────────────────────────────────────────────────┐&#xA;│  test_runner = test.spawn(driver, logger_with_taxonomy)              │&#xA;└──────────────────────────────┬───────────────────────────────────────┘&#xA;                               │&#xA;                               ▼&#xA;              ┌─────────────────────────────────┐&#xA;              │  test_runner.skipped ?          │── True ─► return Outcome(skipped=True, ...)&#xA;              └────────────────┬────────────────┘&#xA;                               │ False&#xA;                               ▼&#xA;              ┌─────────────────────────────────┐&#xA;              │  logger.test_name(test.name)    │   (annotation)&#xA;              └────────────────┬────────────────┘&#xA;                               │&#xA;                               ▼&#xA;              ┌─────────────────────────────────┐&#xA;              │  setup() (if not None)          │── raises ─► teardown() (if not None)&#xA;              └────────────────┬────────────────┘            ↓&#xA;                               │                  return Outcome(setup_failed=True, should_retry=True, ...)&#xA;                               ▼&#xA;              ┌─────────────────────────────────┐&#xA;              │  watchers.start(driver, logger, │&#xA;              │                 take_screenshot)│   (1 daemon thread per watcher)&#xA;              └────────────────┬────────────────┘&#xA;                               │&#xA;                               ▼&#xA;              ┌─────────────────────────────────┐&#xA;              │  _run_chain(chain_runners, ...) │── returns (result, should_retry)&#xA;              └────────────────┬────────────────┘&#xA;                               │&#xA;                               ▼&#xA;              ┌─────────────────────────────────┐&#xA;              │  watchers.stop()                │   (always)&#xA;              └────────────────┬────────────────┘&#xA;                               │&#xA;                               ▼&#xA;              ┌─────────────────────────────────┐&#xA;              │  teardown() (if not None)       │   (ALWAYS, even if chain failed)&#xA;              └────────────────┬────────────────┘     (exceptions are logged &amp;amp; swallowed)&#xA;                               │&#xA;                               ▼&#xA;              ┌─────────────────────────────────┐&#xA;              │  steps_count = act_counter.get()│&#xA;              │  return Outcome(...)            │&#xA;              └─────────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;execute&#34;&gt;&lt;code&gt;execute&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#execute&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;execute&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;font-style:italic&#34;&gt;self&lt;/span&gt;, test: Test[Driver], &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    driver: Driver,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    taxonomy: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;tuple&lt;/span&gt;[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logger_with_taxonomy: ILogger,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logger_without_taxonomy: ILogger,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    attempt: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;, max_attempts: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&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;-&amp;gt;&lt;/span&gt; ExecutionOutcome:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    test_runner &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;spawn(driver, logger_with_taxonomy)&#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;if&lt;/span&gt; test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;skipped:&#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;return&lt;/span&gt; ExecutionOutcome(result&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, skipped&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;, setup_failed&lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;                                should_retry&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;False&lt;/span&gt;, steps_count&lt;span style=&#34;color:#ff79c6&#34;&gt;=-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&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;    logger_without_taxonomy&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;test_name(test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name)&#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;if&lt;/span&gt; test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;setup &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;try&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;setup()&#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;except&lt;/span&gt; Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;as&lt;/span&gt; exc:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            msg &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt; -- Setup failed (attempt &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;attempt&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;max_attempts&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;): &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;exc&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            logger_with_taxonomy&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;warning(msg)&#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; test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;teardown &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_run_teardown(test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;teardown, test_name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name, logger&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;logger_with_taxonomy)&#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;return&lt;/span&gt; ExecutionOutcome(result&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, skipped&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;False&lt;/span&gt;, setup_failed&lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;                                    should_retry&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;, steps_count&lt;span style=&#34;color:#ff79c6&#34;&gt;=-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&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;    watchers: Sequence[Watcher[Driver]] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;watchers &lt;span style=&#34;color:#ff79c6&#34;&gt;or&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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_start_watchers(watchers, driver&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;driver, test_name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name, taxonomy&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;taxonomy)&#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;    result, should_retry &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_run_chain(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;chain_runners, test_name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name, attempt&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;attempt,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        driver&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;driver, logger&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;logger_without_taxonomy,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logger_with_taxonomy&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;logger_with_taxonomy, max_attempts&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;max_attempts,&#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;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_stop_watchers(watchers)&#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;if&lt;/span&gt; test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;teardown &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_run_teardown(test_runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;teardown, test_name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name, logger&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;logger_with_taxonomy)&#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;    steps_count &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_act_counter&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;get()&#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;return&lt;/span&gt; ExecutionOutcome(result&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;result, skipped&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;False&lt;/span&gt;, setup_failed&lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;                            should_retry&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;should_retry, steps_count&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;steps_count)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;two-loggers-why&#34;&gt;Two loggers: why&lt;a class=&#34;anchor&#34; href=&#34;#two-loggers-why&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;logger_with_taxonomy&lt;/code&gt; vs &lt;code&gt;logger_without_taxonomy&lt;/code&gt;:&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.10.02 — DriverBuilder[Driver]</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/10-infra/02-driver-builder/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/10-infra/02-driver-builder/</guid>
      <description>&lt;h1 id=&#34;021002driverbuilderdriver&#34;&gt;02.10.02 — &lt;code&gt;DriverBuilder[Driver]&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#021002driverbuilderdriver&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/infra/driver_builder.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/infra/driver_builder.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Encapsulates &lt;strong&gt;profile&lt;/strong&gt; handling (often a tmp copy of a user directory) and produces the &lt;code&gt;(driver, dispose)&lt;/code&gt; pair the pool expects.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;why-a-builder&#34;&gt;Why a builder?&lt;a class=&#34;anchor&#34; href=&#34;#why-a-builder&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Building a Selenium driver with a &lt;em&gt;profile&lt;/em&gt; requires:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Copy the profile&lt;/strong&gt; to a temp folder (otherwise Firefox/Chrome locks the original).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Start the driver&lt;/strong&gt; pointing at the temp folder.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;At the end&lt;/strong&gt;: &lt;code&gt;driver.quit()&lt;/code&gt;, then delete the temp folder.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;&lt;code&gt;DriverBuilder&lt;/code&gt; factors out those three steps:&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.11.02 — CliStore[TKeys] &#43; _CliField[T] &#43; phantom_validate</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/02-cli-store-phantoms/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/02-cli-store-phantoms/</guid>
      <description>&lt;h1 id=&#34;021102clistoretkeys--_clifieldt--phantom_validate&#34;&gt;02.11.02 — &lt;code&gt;CliStore[TKeys]&lt;/code&gt; + &lt;code&gt;_CliField[T]&lt;/code&gt; + &lt;code&gt;phantom_validate&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#021102clistoretkeys--_clifieldt--phantom_validate&#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;Source files: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/cli/store.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/opinionated/cli/store.py&lt;/code&gt;&lt;/a&gt;, &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/cli/phantoms.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/opinionated/cli/phantoms.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Write-once&lt;/strong&gt; store for parsed CLI values. Each field validates on write. &lt;code&gt;TKeys: Literal[...]&lt;/code&gt; gives key autocomplete.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;_clifieldtwrite-once-field&#34;&gt;&lt;code&gt;_CliField[T]&lt;/code&gt; — write-once field&lt;a class=&#34;anchor&#34; href=&#34;#_clifieldtwrite-once-field&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_CliField&lt;/span&gt;[T]:&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, validate: ValidationChainBuilder[T]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_value: T &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; _Unset &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; _UNSET&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_validate &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; validate&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;set&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, value: T) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;if&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;isinstance&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_value, _Unset):&#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;raise&lt;/span&gt; RuntimeError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Value already set.&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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_validate(_validate(value))&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;raise_if_invalid()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_value &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; value&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; T:&#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; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;isinstance&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_value, _Unset):&#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;raise&lt;/span&gt; RuntimeError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Value not set yet.&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;return&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_value&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;1-_unset-sentinel&#34;&gt;1. &lt;code&gt;_Unset&lt;/code&gt; sentinel&lt;a class=&#34;anchor&#34; href=&#34;#1-_unset-sentinel&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_Unset&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;pass&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;_UNSET &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; _Unset()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Why not &lt;code&gt;None&lt;/code&gt;? Because &lt;code&gt;None&lt;/code&gt; can be a &lt;strong&gt;legitimate value&lt;/strong&gt; (&lt;code&gt;--profile-path&lt;/code&gt; isn&amp;rsquo;t mandatory; its post-parse value can legitimately be &lt;code&gt;None&lt;/code&gt;). A dedicated sentinel separates &amp;ldquo;&lt;em&gt;not set yet&lt;/em&gt;&amp;rdquo; from &amp;ldquo;&lt;em&gt;set to None&lt;/em&gt;&amp;rdquo;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>03.02 — Closures as the primitive of inversion of control</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/02-closures-ioc/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/02-closures-ioc/</guid>
      <description>&lt;h1 id=&#34;0302closures-as-the-primitive-of-inversion-of-control&#34;&gt;03.02 — Closures as the primitive of inversion of control&lt;a class=&#34;anchor&#34; href=&#34;#0302closures-as-the-primitive-of-inversion-of-control&#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;No DI container. No injection framework. &lt;strong&gt;A closure is Ocarina&amp;rsquo;s injection primitive.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;the-idea&#34;&gt;The idea&lt;a class=&#34;anchor&#34; href=&#34;#the-idea&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;A closure captures values from its enclosing scope. Return a function from another function and the outer parameters are &lt;strong&gt;available inside&lt;/strong&gt;, even after the outer function returns.&lt;/p&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;make_handler&lt;/span&gt;(logger: ILogger) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Callable[[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;], FailureHandler]:&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;make_failure_handler&lt;/span&gt;(msg: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; FailureHandler:&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;actual_handler&lt;/span&gt;(exc: Exception) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            logger&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;error(msg, exc&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;exc)&#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;return&lt;/span&gt; actual_handler&#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;return&lt;/span&gt; make_failure_handler&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Also &lt;em&gt;currying&lt;/em&gt;: &lt;code&gt;make_handler(logger)(msg)(exc)&lt;/code&gt;. Each call captures a new layer of environment.&lt;/p&gt;</description>
    </item>
    <item>
      <title>04.02 — Cram tests (prysk)</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/04-internal-tests/02-cram-prysk/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/04-internal-tests/02-cram-prysk/</guid>
      <description>&lt;h1 id=&#34;0402cram-tests-prysk&#34;&gt;04.02 — Cram tests (&lt;code&gt;prysk&lt;/code&gt;)&lt;a class=&#34;anchor&#34; href=&#34;#0402cram-tests-prysk&#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;CLI tests in &lt;em&gt;cram&lt;/em&gt; format: a &lt;code&gt;.t&lt;/code&gt; file contains shell commands and their expected output. Tool: &lt;code&gt;prysk&lt;/code&gt; (modern rewrite of &lt;code&gt;cram&lt;/code&gt;).&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;t-file&#34;&gt;&lt;code&gt;.t&lt;/code&gt; file&lt;a class=&#34;anchor&#34; href=&#34;#t-file&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Valid args produce a deterministic parsed config&#xA;&#xA;  $ touch driver&#xA;  $ &amp;#34;$PYTHON&amp;#34; &amp;#34;$TESTDIR/_demo_cli.py&amp;#34; --browser firefox --driver-path driver --workers 3 --wait-timeout 20 --logger terminal&#xA;  browser=firefox&#xA;  driver_path=driver&#xA;  headless=True&#xA;  workers=3&#xA;  wait_timeout=20&#xA;  logger=terminal&#xA;  only=()&#xA;  exclude=()&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#xA;&lt;li&gt;First line: title (comment for the human).&lt;/li&gt;&#xA;&lt;li&gt;Blank line.&lt;/li&gt;&#xA;&lt;li&gt;Lines starting with &lt;code&gt; $&lt;/code&gt;: shell command run.&lt;/li&gt;&#xA;&lt;li&gt;Following lines (indent 2): expected output.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Actual output differs → test fails.&lt;/p&gt;</description>
    </item>
    <item>
      <title>05.02 — Igoristan routes</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/05-igoristan/02-routes/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/05-igoristan/02-routes/</guid>
      <description>&lt;h1 id=&#34;0502igoristan-routes&#34;&gt;05.02 — Igoristan routes&lt;a class=&#34;anchor&#34; href=&#34;#0502igoristan-routes&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;srcconfigroutests&#34;&gt;&lt;code&gt;src/config/routes.ts&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#srcconfigroutests&#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-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt; __ROOT &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;/igoristan/&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;const&lt;/span&gt; DASHBOARD &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;dashboard&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:#ff79c6&#34;&gt;const&lt;/span&gt; __ROUTES &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  DONKEY_SAUSAGE_DETECTOR&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;donkey-sausage-eater-detector&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  DASHBOARD_NESTED&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;${&lt;/span&gt;DASHBOARD&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;/nested`&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  RANDOM_LOADERS&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;random-loaders&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  SACRED_UPLOAD&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;sacred-upload&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  RANDOM_ERROR&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;random-error&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  CHAOTIC_FORM&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;chaotic-form&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  CORSICAMON&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;corsicamon&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  MADNESS&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;madness&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  DASHBOARD,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  HOME&lt;span style=&#34;color:#ff79c6&#34;&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:#ff79c6&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt; satisfies Routes;&#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;const&lt;/span&gt; ROUTES &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; createRoutes(__ROUTES, __ROOT);&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;Route&lt;/th&gt;&#xA;          &lt;th&gt;URL&lt;/th&gt;&#xA;          &lt;th&gt;Role&lt;/th&gt;&#xA;          &lt;th&gt;Deliberate chaos&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;HOME&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/igoristan/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Home with links to other pages, autoplay audio&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;RANDOM_LOADERS&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/igoristan/random-loaders&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Pseudo-random loaders&lt;/td&gt;&#xA;          &lt;td&gt;Loaders that stay visible for a variable time&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;SACRED_UPLOAD&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/igoristan/sacred-upload&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;react-dropzone&lt;/code&gt; upload form&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;DASHBOARD&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/igoristan/dashboard&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Login (password &lt;code&gt;figatellu&lt;/code&gt;), optional MFA OTP&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;useAuth&lt;/code&gt; may fail 10% of the time even with the right pwd&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;DASHBOARD_NESTED&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/igoristan/dashboard/nested&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Protected page, requires MFA&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;CORSICAMON&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/igoristan/corsicamon&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;Corsican Pokédex&lt;/em&gt;&amp;rdquo;, 3 random picks via API key&lt;/td&gt;&#xA;          &lt;td&gt;1/5 chance of artificially raising &lt;code&gt;Error(&#39;lol&#39;)&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;RANDOM_ERROR&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/igoristan/random-error&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Fake error page&lt;/td&gt;&#xA;          &lt;td&gt;Title matched by &lt;code&gt;ERROR_PAGE_REGEX&lt;/code&gt; on the test side&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;CHAOTIC_FORM&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/igoristan/chaotic-form&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Unstable form&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;.catch-me-if-you-can&lt;/code&gt; elements appear at random&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;MADNESS&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/igoristan/madness&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Alternating stories (Cors, ThisIsBastia)&lt;/td&gt;&#xA;          &lt;td&gt;Renders one OR the other at random&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;DONKEY_SAUSAGE_DETECTOR&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/igoristan/donkey-sausage-eater-detector&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Detector for filthy Sicilian shits&lt;/td&gt;&#xA;          &lt;td&gt;30% random fail (BSOD-style)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;page-by-page&#34;&gt;Page by page&lt;a class=&#34;anchor&#34; href=&#34;#page-by-page&#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-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt; links &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  { href: &lt;span style=&#34;color:#8be9fd&#34;&gt;ROUTES.RANDOM_LOADERS&lt;/span&gt;, label&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Random loaders&amp;#34;&lt;/span&gt; },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  { href: &lt;span style=&#34;color:#8be9fd&#34;&gt;ROUTES.SACRED_UPLOAD&lt;/span&gt;, label&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Sacred upload&amp;#34;&lt;/span&gt; },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  { href: &lt;span style=&#34;color:#8be9fd&#34;&gt;ROUTES.DASHBOARD&lt;/span&gt;, label&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Dashboard&amp;#34;&lt;/span&gt; },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  { href: &lt;span style=&#34;color:#8be9fd&#34;&gt;ROUTES.CORSICAMON&lt;/span&gt;, label&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Corsicamon&amp;#34;&lt;/span&gt; },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  { href: &lt;span style=&#34;color:#8be9fd&#34;&gt;ROUTES.RANDOM_ERROR&lt;/span&gt;, label&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Random Error&amp;#34;&lt;/span&gt; },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  { href: &lt;span style=&#34;color:#8be9fd&#34;&gt;ROUTES.CHAOTIC_FORM&lt;/span&gt;, label&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Chaotic form&amp;#34;&lt;/span&gt; },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  { href: &lt;span style=&#34;color:#8be9fd&#34;&gt;ROUTES.MADNESS&lt;/span&gt;, label&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Madness&amp;#34;&lt;/span&gt; },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  { href: &lt;span style=&#34;color:#8be9fd&#34;&gt;ROUTES.DONKEY_SAUSAGE_DETECTOR&lt;/span&gt;, label&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Donkey Sausage Detector&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;h3 id=&#34;home&#34;&gt;home&lt;a class=&#34;anchor&#34; href=&#34;#home&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;8 links to the other pages + autoplay audio Angelus of Jerusalem (amen 🙏).&lt;/p&gt;</description>
    </item>
    <item>
      <title>06.02 — GET /api/otp</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/06-tests-workers/02-otp-endpoint/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/06-tests-workers/02-otp-endpoint/</guid>
      <description>&lt;h1 id=&#34;0602get-apiotp&#34;&gt;06.02 — &lt;code&gt;GET /api/otp&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#0602get-apiotp&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/tests-workers/blob/main/api/otp.ts&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;api/otp.ts&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; { generate } &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;otplib&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;import&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;type&lt;/span&gt; { NextRequest } &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;next/server&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;import&lt;/span&gt; redis &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;../lib/redis&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;import&lt;/span&gt; isAuthorized &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;../lib/isAuthorized&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:#ff79c6&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt; config &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; { runtime&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;edge&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:#ff79c6&#34;&gt;const&lt;/span&gt; SECRET_QUERY_PARAM_KEY &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;secret&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;const&lt;/span&gt; OTP_SECRET_MIN_LENGTH &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;32&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;const&lt;/span&gt; OTP_SECRET_MAX_LENGTH &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;64&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;const&lt;/span&gt; BASE32_ALPHABET &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;ABCDEFGHIJKLMNOPQRSTUVWXYZ234567&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;const&lt;/span&gt; OTP_TTL &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;60&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;; &lt;span style=&#34;color:#6272a4&#34;&gt;// 5 minutes&#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;const&lt;/span&gt; OTP_HISTORY_TTL &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; OTP_TTL &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;60&lt;/span&gt;; &lt;span style=&#34;color:#6272a4&#34;&gt;// 6 minutes&#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;const&lt;/span&gt; MAX_FREE_PAYLOAD_SIZE &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;4096&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;const&lt;/span&gt; corsHeaders &lt;span style=&#34;color:#ff79c6&#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:#f1fa8c&#34;&gt;&amp;#34;Access-Control-Allow-Origin&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&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;Access-Control-Allow-Methods&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;GET, OPTIONS&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;Access-Control-Allow-Headers&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;x-api-key, Content-Type&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;as&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;const&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:#8be9fd;font-style:italic&#34;&gt;function&lt;/span&gt; generateRandomBase32Secret(length: &lt;span style=&#34;color:#8be9fd&#34;&gt;number&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;32&lt;/span&gt;)&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#8be9fd&#34;&gt;string&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:#8be9fd;font-style:italic&#34;&gt;let&lt;/span&gt; secret &lt;span style=&#34;color:#ff79c6&#34;&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:#ff79c6&#34;&gt;const&lt;/span&gt; randomValues &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Uint8Array(length);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  crypto.getRandomValues(randomValues);&#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;for&lt;/span&gt; (&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;let&lt;/span&gt; i &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;; i &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;&lt;/span&gt; length; i&lt;span style=&#34;color:#ff79c6&#34;&gt;++&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    secret &lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt; BASE32_ALPHABET[randomValues[i] &lt;span style=&#34;color:#ff79c6&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;32&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;return&lt;/span&gt; secret;&#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;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;function&lt;/span&gt; ensureSecretLength(secret: &lt;span style=&#34;color:#8be9fd&#34;&gt;string&lt;/span&gt;)&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#8be9fd&#34;&gt;string&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;const&lt;/span&gt; cleaned &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; secret.toUpperCase().replace(&lt;span style=&#34;color:#f1fa8c&#34;&gt;/\s/g&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:#ff79c6&#34;&gt;if&lt;/span&gt; (cleaned.length &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; OTP_SECRET_MAX_LENGTH) {&#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;const&lt;/span&gt; validLength &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;Math&lt;/span&gt;.floor(OTP_SECRET_MAX_LENGTH &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;8&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;8&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;return&lt;/span&gt; cleaned.substring(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, validLength);&#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;if&lt;/span&gt; (cleaned.length &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;=&lt;/span&gt; OTP_SECRET_MIN_LENGTH) {&#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;const&lt;/span&gt; validLength &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;Math&lt;/span&gt;.floor(cleaned.length &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;8&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;8&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;return&lt;/span&gt; cleaned.substring(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, validLength);&#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;const&lt;/span&gt; repetitions &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;Math&lt;/span&gt;.ceil(OTP_SECRET_MIN_LENGTH &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; cleaned.length);&#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;const&lt;/span&gt; repeated &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; cleaned.repeat(repetitions);&#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;const&lt;/span&gt; validLength &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;Math&lt;/span&gt;.floor(repeated.length &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;8&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;8&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;return&lt;/span&gt; repeated.substring(&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;Math&lt;/span&gt;.max(validLength, OTP_SECRET_MIN_LENGTH));&#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;&#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;export&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;function&lt;/span&gt; handler(request: &lt;span style=&#34;color:#8be9fd&#34;&gt;NextRequest&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;if&lt;/span&gt; (request.method &lt;span style=&#34;color:#ff79c6&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;OPTIONS&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;return&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Response(&lt;span style=&#34;color:#ff79c6&#34;&gt;null&lt;/span&gt;, { status: &lt;span style=&#34;color:#8be9fd&#34;&gt;200&lt;/span&gt;, headers: &lt;span style=&#34;color:#8be9fd&#34;&gt;corsHeaders&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;&#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; (&lt;span style=&#34;color:#ff79c6&#34;&gt;!&lt;/span&gt;isAuthorized(request)) {&#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;return&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Response(JSON.stringify({ error&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Unauthorized&amp;#34;&lt;/span&gt; }), {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      status: &lt;span style=&#34;color:#8be9fd&#34;&gt;401&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      headers&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; { &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;, ...corsHeaders },&#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;  }&#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;const&lt;/span&gt; { searchParams } &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; URL(request.url);&#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;const&lt;/span&gt; freePayload: &lt;span style=&#34;color:#8be9fd&#34;&gt;Record&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#ff79c6&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#50fa7b&#34;&gt;string&lt;/span&gt;&amp;gt; &lt;span style=&#34;color:#ff79c6&#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:#8be9fd;font-style:italic&#34;&gt;let&lt;/span&gt; payloadSize &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&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;  searchParams.forEach((value, key) &lt;span style=&#34;color:#ff79c6&#34;&gt;=&amp;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;if&lt;/span&gt; (key &lt;span style=&#34;color:#ff79c6&#34;&gt;===&lt;/span&gt; SECRET_QUERY_PARAM_KEY) &lt;span style=&#34;color:#ff79c6&#34;&gt;return&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;const&lt;/span&gt; entrySize &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; TextEncoder().encode(key &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; value).length;&#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; (payloadSize &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; entrySize &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; MAX_FREE_PAYLOAD_SIZE) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      payloadSize &lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt; entrySize;&#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;return&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;    freePayload[key] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; value;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    payloadSize &lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt; entrySize;&#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;&#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; (payloadSize &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; MAX_FREE_PAYLOAD_SIZE) {&#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;return&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Response(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      JSON.stringify({&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        error&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Bad Request&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        message&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;`Free payload exceeds maximum size of &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;${&lt;/span&gt;MAX_FREE_PAYLOAD_SIZE&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt; bytes (got &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;${&lt;/span&gt;payloadSize&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt; bytes)`&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;      {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        status: &lt;span style=&#34;color:#8be9fd&#34;&gt;400&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        headers&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; { &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;, ...corsHeaders },&#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;    );&#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;&#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;const&lt;/span&gt; rawSecret &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    searchParams.&lt;span style=&#34;color:#ff79c6&#34;&gt;get&lt;/span&gt;(SECRET_QUERY_PARAM_KEY) &lt;span style=&#34;color:#ff79c6&#34;&gt;??&lt;/span&gt; generateRandomBase32Secret();&#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;const&lt;/span&gt; secret &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; ensureSecretLength(rawSecret);&#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;const&lt;/span&gt; otpCode &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;await&lt;/span&gt; generate({ secret });&#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;const&lt;/span&gt; now &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;Date&lt;/span&gt;.now();&#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;const&lt;/span&gt; expiresAtMs &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; now &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; OTP_TTL &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1000&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;const&lt;/span&gt; createdAtTimestampLackingMsPrecision &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;Date&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    now &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt; (now &lt;span style=&#34;color:#ff79c6&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1000&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ).toISOString();&#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;const&lt;/span&gt; event &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...freePayload,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    otpCode,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    secret,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    createdAtTimestampLackingMsPrecision,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    expiresAt: &lt;span style=&#34;color:#8be9fd&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;Date&lt;/span&gt;(expiresAtMs).toISOString(),&#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;&#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;const&lt;/span&gt; key &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;`otp:&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;${&lt;/span&gt;now&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;${&lt;/span&gt;crypto.randomUUID()&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#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;await&lt;/span&gt; redis.&lt;span style=&#34;color:#ff79c6&#34;&gt;set&lt;/span&gt;(key, JSON.stringify(event), { ex: &lt;span style=&#34;color:#8be9fd&#34;&gt;OTP_HISTORY_TTL&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;return&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Response(JSON.stringify(event), {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    headers&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; { &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;, ...corsHeaders },&#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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;flow&#34;&gt;Flow&lt;a class=&#34;anchor&#34; href=&#34;#flow&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;1. Request arrives&#xA;   ↓&#xA;2. If OPTIONS → 200 + CORS headers (preflight)&#xA;   ↓&#xA;3. isAuthorized(request)? No → 401&#xA;   ↓&#xA;4. Parse searchParams; everything except &amp;#34;secret&amp;#34; goes into freePayload&#xA;   ↓&#xA;5. payloadSize &amp;gt; 4 KB? → 400&#xA;   ↓&#xA;6. secret = ?secret=&amp;lt;X&amp;gt; (if given) else random Base32 32-chars&#xA;   ↓&#xA;7. ensureSecretLength: enforces 32..64 chars, multiple of 8&#xA;   ↓&#xA;8. otpCode = generate({ secret })   ← otplib TOTP RFC 6238&#xA;   ↓&#xA;9. createdAt = floor(now / 1000) * 1000  ← ms stripped&#xA;   ↓&#xA;10. event = { ...freePayload, otpCode, secret, createdAt, expiresAt }&#xA;    ↓&#xA;11. key = `otp:&amp;lt;now&amp;gt;:&amp;lt;uuid&amp;gt;`&#xA;    ↓&#xA;12. redis.set(key, JSON.stringify(event), { ex: 360 })  ← TTL 6 min&#xA;    ↓&#xA;13. Response = JSON.stringify(event), 200&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;cors-headers&#34;&gt;CORS headers&lt;a class=&#34;anchor&#34; href=&#34;#cors-headers&#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-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt; corsHeaders &lt;span style=&#34;color:#ff79c6&#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:#f1fa8c&#34;&gt;&amp;#34;Access-Control-Allow-Origin&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&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;Access-Control-Allow-Methods&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;GET, OPTIONS&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;Access-Control-Allow-Headers&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;x-api-key, Content-Type&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;as&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;*&lt;/code&gt; origin: lets Igoristan (&lt;code&gt;https://mojo-molotov.github.io&lt;/code&gt;) call the service.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;GET, OPTIONS&lt;/code&gt;: the only supported verbs.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;x-api-key&lt;/code&gt; listed explicitly — otherwise the browser blocks it as a non-simple header.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;code&gt;as const&lt;/code&gt; makes the object readonly on the TypeScript side.&lt;/p&gt;</description>
    </item>
    <item>
      <title>07.02 — The 5 adapters</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/02-adapters/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/02-adapters/</guid>
      <description>&lt;h1 id=&#34;0702the-5-adapters&#34;&gt;07.02 — The 5 adapters&lt;a class=&#34;anchor&#34; href=&#34;#0702the-5-adapters&#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;Every Ocarina project starts by writing &lt;strong&gt;five&lt;/strong&gt; adapters on top of the framework.&lt;br&gt;&#xA;The &lt;code&gt;ocarina-example&lt;/code&gt; project provides them as reference.&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;Adapter&lt;/th&gt;&#xA;          &lt;th&gt;Status&lt;/th&gt;&#xA;          &lt;th&gt;File&lt;/th&gt;&#xA;          &lt;th&gt;Purpose&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;act&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;required&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;lib/ext/ocarina/adapters/agnostic/act.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Project-specific &lt;code&gt;on_failure&lt;/code&gt; hook&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;match_page&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;optional&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;lib/ext/ocarina/adapters/agnostic/match_page.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Shared exception policy&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;EnvGetters&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;optional&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;lib/ext/ocarina/adapters/agnostic/env_getters.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Typed access to env vars&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestSuite&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;required&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;lib/ext/ocarina/adapters/selenium/test_suite.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Façade: pins &lt;code&gt;transient_errors&lt;/code&gt;, &lt;code&gt;max_retries&lt;/code&gt;, &lt;code&gt;autoscreen&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestCampaign&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;required&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;lib/ext/ocarina/adapters/selenium/test_campaign.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Façade: &lt;code&gt;max_workers&lt;/code&gt; from CLI&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;act&#34;&gt;&lt;code&gt;act&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#act&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# lib/ext/ocarina/adapters/agnostic/act.py&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;from&lt;/span&gt; contextlib &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; suppress&#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;from&lt;/span&gt; ocarina.dsl.testing_with_railway.constructors.create_act &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; create_act&#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;from&lt;/span&gt; ocarina.railway.result &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; Fail&#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;from&lt;/span&gt; lib.custom_errors.http &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; HttpErrorPageReachedError&#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;from&lt;/span&gt; lib.ext.ocarina.regex.error_page &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; ERROR_PAGE_REGEX&#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;&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;act&lt;/span&gt;(pom: TPOM, action: Callable[[TPOM], TPOM]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; ActionStart[TPOM]:&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;failure_hook&lt;/span&gt;(pom: TPOM, exc: Exception) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Fail:&#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; suppress(Exception):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            title &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; pom&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;get_current_title()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            is_http_error_page &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; title &lt;span style=&#34;color:#ff79c6&#34;&gt;and&lt;/span&gt; ERROR_PAGE_REGEX&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;match&lt;/span&gt;(title&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;strip())&#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; is_http_error_page:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                http_error &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; HttpErrorPageReachedError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;HTTP error page: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;title&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                http_error&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;__cause__ &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; exc&#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;return&lt;/span&gt; Fail(error&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;http_error)&#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;return&lt;/span&gt; Fail(error&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;exc)&#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;return&lt;/span&gt; create_act(pom, action, on_failure&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;failure_hook)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;get_current_title()&lt;/code&gt;&lt;/strong&gt;: uses &lt;code&gt;POMBase&lt;/code&gt;&amp;rsquo;s mandatory method (see &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/08-pom-base/&#34;&gt;&lt;code&gt;../02-ocarina/08-pom-base.md&lt;/code&gt;&lt;/a&gt;).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;ERROR_PAGE_REGEX&lt;/code&gt;&lt;/strong&gt;: detects titles shaped like HTTP error pages (&lt;code&gt;^\d{3}\s.*&lt;/code&gt; → &lt;code&gt;500 Internal Server Error&lt;/code&gt;).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;HttpErrorPageReachedError&lt;/code&gt;&lt;/strong&gt;: project exception, included in &lt;code&gt;transient_errors&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;This adapter is what makes error-page replay &lt;strong&gt;automatic&lt;/strong&gt;.&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>09.02 — I18n FR/EN/RU</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/02-i18n/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/02-i18n/</guid>
      <description>&lt;h1 id=&#34;0902i18n-frenru&#34;&gt;09.02 — I18n FR/EN/RU&lt;a class=&#34;anchor&#34; href=&#34;#0902i18n-frenru&#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;Every Holy Book page exists in three versions. EN at the root of &lt;code&gt;docs/&lt;/code&gt;, FR under &lt;code&gt;docs/fr/&lt;/code&gt;, RU under &lt;code&gt;docs/ru/&lt;/code&gt;. VitePress routes automatically.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;file-structure&#34;&gt;File structure&lt;a class=&#34;anchor&#34; href=&#34;#file-structure&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;docs/&#xA;├── index.md                                                        # EN home&#xA;├── what-is-it.md&#xA;├── first-feedbacks.md&#xA;├── setup.md&#xA;├── scenarios-composability.md&#xA;├── datasets-smoke-tests-setup-teardown-proxy-api-and-caching.md&#xA;├── handling-flakiness.md&#xA;├── extensibility.md&#xA;├── using-ocarina-with-ai.md&#xA;│&#xA;├── fr/&#xA;│   ├── index.md                                                    # FR home&#xA;│   ├── what-is-it.md&#xA;│   ├── first-feedbacks.md&#xA;│   ├── setup.md&#xA;│   ├── scenarios-composability.md&#xA;│   ├── datasets-smoke-tests-setup-teardown-proxy-api-and-caching.md&#xA;│   ├── handling-flakiness.md&#xA;│   ├── extensibility.md&#xA;│   └── using-ocarina-with-ai.md&#xA;│&#xA;├── ru/&#xA;│   ├── index.md                                                    # RU home&#xA;│   ├── what-is-it.md&#xA;│   ├── first-feedbacks.md&#xA;│   ├── setup.md&#xA;│   ├── scenarios-composability.md&#xA;│   ├── datasets-smoke-tests-setup-teardown-proxy-api-and-caching.md&#xA;│   ├── handling-flakiness.md&#xA;│   ├── extensibility.md&#xA;│   └── using-ocarina-with-ai.md&#xA;│&#xA;├── public/&#xA;│   ├── favicon.ico&#xA;│   ├── logo.png&#xA;│   ├── robots.txt&#xA;│   ├── .nojekyll&#xA;│   ├── .spa&#xA;│   └── assets/&#xA;│       └── content/&#xA;│           └── ...                                                 # shared EN+FR+RU images&#xA;│&#xA;└── .vitepress/&#xA;    ├── config.mts&#xA;    ├── blog-theme.ts&#xA;    ├── shims.d.ts&#xA;    ├── plugins/&#xA;    │   ├── llm.ts&#xA;    │   ├── pub.ts&#xA;    │   ├── skills.ts&#xA;    │   └── webp.ts&#xA;    └── theme/&#xA;        ├── index.ts&#xA;        ├── style.css&#xA;        ├── user-theme.css&#xA;        ├── components/&#xA;        │   ├── Pagination.vue&#xA;        │   ├── BlogHomeBanner.vue&#xA;        │   └── NotFound.vue&#xA;        └── assets/&#xA;            └── bg.png&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;routing&#34;&gt;Routing&lt;a class=&#34;anchor&#34; href=&#34;#routing&#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;URL EN&lt;/th&gt;&#xA;          &lt;th&gt;URL FR&lt;/th&gt;&#xA;          &lt;th&gt;URL RU&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Home&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/fr/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/ru/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Pitch&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/what-is-it&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/fr/what-is-it&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/ru/what-is-it&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Rant&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/first-feedbacks&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/fr/first-feedbacks&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/ru/first-feedbacks&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Setup&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/setup&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/fr/setup&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;/ru/setup&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&amp;hellip;&lt;/td&gt;&#xA;          &lt;td&gt;&amp;hellip;&lt;/td&gt;&#xA;          &lt;td&gt;&amp;hellip;&lt;/td&gt;&#xA;          &lt;td&gt;&amp;hellip;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;VitePress auto-builds the language switcher (top right). Clicking &amp;ldquo;&lt;em&gt;FR&lt;/em&gt;&amp;rdquo; from &lt;code&gt;/setup&lt;/code&gt; takes you to &lt;code&gt;/fr/setup&lt;/code&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>09.03.02 — Analyse skills</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/02-analyse/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/02-analyse/</guid>
      <description>&lt;h1 id=&#34;090302analyse-skills&#34;&gt;09.03.02 — Analyse skills&lt;a class=&#34;anchor&#34; href=&#34;#090302analyse-skills&#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;&lt;strong&gt;Dynamic&lt;/strong&gt; analyses: use the logs / reports of a recent execution to diagnose a failure. Two root-cause skills sit in front of the family — &lt;code&gt;diagnose-root-cause&lt;/code&gt; for a deterministic red, &lt;code&gt;diagnose-flake-root-cause&lt;/code&gt; for an intermittent one — and route to the controlled &lt;code&gt;analyse-*&lt;/code&gt; experiments.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;listing-potentially-non-exhaustive&#34;&gt;Listing (potentially non-exhaustive)&lt;a class=&#34;anchor&#34; href=&#34;#listing-potentially-non-exhaustive&#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;Skill&lt;/th&gt;&#xA;          &lt;th&gt;Target&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;diagnose-root-cause&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Structured RCA for a &lt;strong&gt;deterministic&lt;/strong&gt; red: re-derive, the synthetic-to-real ladder, root cause sorted into five buckets&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;diagnose-flake-root-cause&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Structured RCA for an &lt;strong&gt;intermittent&lt;/strong&gt; failure (a flake): failure rate, signature, correlation; orchestrates the &lt;code&gt;analyse-*&lt;/code&gt; experiments&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;analyse-flakiness&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Widens the &lt;code&gt;transient_errors&lt;/code&gt; net; chronic deaths are real flakes&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;analyse-fixture-flakiness&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Instruments setup/teardown; surfaces cross-test contaminations&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;analyse-watcher-flakiness&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Analyzes watcher reliability (volume, dedup, false positives)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;analyse-screenshot-flakiness&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Groups screenshots by &lt;code&gt;(test, step, browser)&lt;/code&gt;, detects differences&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;diagnose-root-cause&#34;&gt;&lt;code&gt;diagnose-root-cause&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#diagnose-root-cause&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;input  : a deterministic red (fails every run)&#xA;output : structured root-cause analysis:&#xA;            - re-derive the failure, don&amp;#39;t inherit prior assumptions&#xA;            - climb the synthetic-to-real ladder (fake driver → real browser)&#xA;            - read the source, confirm with a probe&#xA;            - root cause sorted into five buckets&#xA;         hands off to diagnose-flake-root-cause if the failure proves intermittent&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;diagnose-flake-root-cause&#34;&gt;&lt;code&gt;diagnose-flake-root-cause&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#diagnose-flake-root-cause&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;input  : an intermittent failure (a flake)&#xA;output : distribution-based root-cause analysis:&#xA;            - establish a failure rate (the distribution is the evidence)&#xA;            - pin the signature, correlate&#xA;            - route to the right analyse-* experiment, raise the rate to confirm&#xA;            - classify into five flake buckets&#xA;         the orchestrator of the analyse-* family&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;analyse-flakiness&#34;&gt;&lt;code&gt;analyse-flakiness&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#analyse-flakiness&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;input  : logs of the last N runs&#xA;output : list of tests that retry often (by % of retries)&#xA;         + recommendation:&#xA;            - add an exception to the transient_errors tuple&#xA;            - investigate a particular test (the issue might come from the test itself)&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;analyse-fixture-flakiness&#34;&gt;&lt;code&gt;analyse-fixture-flakiness&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#analyse-fixture-flakiness&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;input  : tests with setup/teardown&#xA;output : tests whose setup fails often&#xA;         + recommendation:&#xA;            - verify setup idempotency&#xA;            - check teardown is clean (otherwise cross-test contamination)&#xA;            - move the seed out of setup into a manual init if too fragile&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;analyse-watcher-flakiness&#34;&gt;&lt;code&gt;analyse-watcher-flakiness&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#analyse-watcher-flakiness&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;input  : watcher.report(...) logs over N runs&#xA;output : watchers with abnormal reporting:&#xA;            - too many emissions (dedup issue)&#xA;            - no emissions (the watcher is useless)&#xA;            - false positives (expected element reported as intruder)&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;analyse-screenshot-flakiness&#34;&gt;&lt;code&gt;analyse-screenshot-flakiness&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#analyse-screenshot-flakiness&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;input  : screenshots from N runs, grouped by (test, step, browser)&#xA;output : visual flakiness:&#xA;            - tests whose screenshots vary abnormally between runs&#xA;            - tests that pass but whose look changes&#xA;            - identify whether the variation is semantic (real diff) or cosmetic&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Uses heuristics (hash, dimensions, dominant palette). Not a real image diff.&lt;/p&gt;</description>
    </item>
    <item>
      <title>10.02 — ocarina workflows (framework)</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/02-ocarina-workflows/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/02-ocarina-workflows/</guid>
      <description>&lt;h1 id=&#34;1002ocarina-workflows-framework&#34;&gt;10.02 — &lt;code&gt;ocarina&lt;/code&gt; workflows (framework)&lt;a class=&#34;anchor&#34; href=&#34;#1002ocarina-workflows-framework&#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 workflows: &lt;code&gt;main_ci.yml&lt;/code&gt; (production), &lt;code&gt;dev_ci.yml&lt;/code&gt; (dev/feature/fix branches), &lt;code&gt;unstable_python_full_build.yml&lt;/code&gt; (monthly cron on Python 3.15-dev).&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;main_ciyml&#34;&gt;&lt;code&gt;main_ci.yml&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#main_ciyml&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;trigger&#34;&gt;Trigger&lt;a class=&#34;anchor&#34; href=&#34;#trigger&#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;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;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;workflow_dispatch&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;inputs&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_versions&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;description&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Python versions to test&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;type&lt;/span&gt;: choice&#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;required&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;default&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Stable only&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;options&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;Stable only&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;Stable + unstable (uses pre-built wheels)&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;workflow_dispatch&lt;/code&gt; with a Python-version choice. Lets the user &lt;em&gt;trigger&lt;/em&gt; a build with 3.15-dev (&lt;em&gt;pre-built wheels only&lt;/em&gt;) without waiting for the monthly cron.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.02 — Auditability in an afternoon</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/11-independence/02-auditability/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/11-independence/02-auditability/</guid>
      <description>&lt;h1 id=&#34;1102auditability-in-an-afternoon&#34;&gt;11.02 — Auditability &amp;ldquo;&lt;em&gt;in an afternoon&lt;/em&gt;&amp;rdquo;&lt;a class=&#34;anchor&#34; href=&#34;#1102auditability-in-an-afternoon&#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;Operational commitment: a human must be able to &lt;em&gt;read&lt;/em&gt; the whole framework and understand it in a few hours. No hidden magic.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;commitment&#34;&gt;Commitment&lt;a class=&#34;anchor&#34; href=&#34;#commitment&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Holy Book quote (chapter &amp;ldquo;&lt;em&gt;What is Ocarina?&lt;/em&gt;&amp;rdquo;):&lt;/p&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;For teams constrained by &lt;em&gt;security policies&lt;/em&gt;: the code is small, auditable in an afternoon. Nothing hidden. The only external dependencies live in the post-execution plugins and if one of them doesn&amp;rsquo;t fit, it can be removed without breaking anything else.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.02 — LulzSec, the Lulzboat, AntiSec</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/02-lulzsec-lulzboat-antisec/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/02-lulzsec-lulzboat-antisec/</guid>
      <description>&lt;h1 id=&#34;1202lulzsec-the-lulzboat-antisec&#34;&gt;12.02 — LulzSec, the Lulzboat, AntiSec&lt;a class=&#34;anchor&#34; href=&#34;#1202lulzsec-the-lulzboat-antisec&#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 distinct subjects: (1) &lt;strong&gt;LulzSec&lt;/strong&gt; as a group, (2) &lt;strong&gt;the Lulzboat&lt;/strong&gt; as identity metaphor, (3) &lt;strong&gt;AntiSec&lt;/strong&gt; as a movement (two versions: 1999 and 2011). The Holy Book invokes them in one breath; each has its own history.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;1-antisec-1999the-original-movement&#34;&gt;1. AntiSec (1999) — the original movement&lt;a class=&#34;anchor&#34; href=&#34;#1-antisec-1999the-original-movement&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;origin&#34;&gt;Origin&lt;a class=&#34;anchor&#34; href=&#34;#origin&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;In the late 1990s, the infosec industry &lt;em&gt;professionalized&lt;/em&gt;: companies formed around selling firewalls, antiviruses, audits. A norm emerged: &lt;strong&gt;&lt;em&gt;full disclosure&lt;/em&gt;&lt;/strong&gt;, publicly releasing vulnerabilities, their PoCs, their exploits, with the argument that it forces vendors to patch quickly.&lt;/p&gt;</description>
    </item>
    <item>
      <title>99.02 — Index of cited files</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/99-references/02-file-index/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/99-references/02-file-index/</guid>
      <description>&lt;h1 id=&#34;9902index-of-cited-files&#34;&gt;99.02 — Index of cited files&lt;a class=&#34;anchor&#34; href=&#34;#9902index-of-cited-files&#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;Per repo, the source files explicitly cited in the primer. Direct GitHub URLs.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;mojo-molotovocarinaframework&#34;&gt;&lt;code&gt;mojo-molotov/ocarina&lt;/code&gt; — Framework&lt;a class=&#34;anchor&#34; href=&#34;#mojo-molotovocarinaframework&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;root&#34;&gt;Root&lt;a class=&#34;anchor&#34; href=&#34;#root&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/pyproject.toml&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;pyproject.toml&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/Makefile&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;Makefile&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/mypy.ini&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;mypy.ini&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/.pre-commit-config.yaml&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;.pre-commit-config.yaml&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/allurerc.mjs&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;allurerc.mjs&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/README.md&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;srcocarinarailway&#34;&gt;&lt;code&gt;src/ocarina/railway/&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#srcocarinarailway&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/railway/result.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;result.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;srcocarinacustom_types&#34;&gt;&lt;code&gt;src/ocarina/custom_types/&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#srcocarinacustom_types&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/custom_types/effect.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;effect.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/custom_types/thunk.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;thunk.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/custom_types/tpom.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;tpom.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/custom_types/scenario.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;scenario.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/custom_types/test_components.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;test_components.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/custom_types/test_runner.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;test_runner.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/custom_types/oc_test_layers.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;oc_test_layers.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;srcocarinacustom_errorstest_framework&#34;&gt;&lt;code&gt;src/ocarina/custom_errors/test_framework/&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#srcocarinacustom_errorstest_framework&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/custom_errors/test_framework/no_matching_branch.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;no_matching_branch.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/custom_errors/test_framework/driver_died.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;driver_died.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/custom_errors/test_framework/pages.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;pages.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;srcocarinacustom_invariantstesting&#34;&gt;&lt;code&gt;src/ocarina/custom_invariants/testing/&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#srcocarinacustom_invariantstesting&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/custom_invariants/testing/workers.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;workers.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/custom_invariants/testing/oc_test_runners_ids.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;oc_test_runners_ids.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/custom_invariants/testing/oc_test_runners_names.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;oc_test_runners_names.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/custom_invariants/testing/oc_test_suites_names.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;oc_test_suites_names.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/custom_invariants/testing/oc_test_campaigns_names.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;oc_test_campaigns_names.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/custom_invariants/testing/oc_test_cycles_names.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;oc_test_cycles_names.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;srcocarinadslinvariants&#34;&gt;&lt;code&gt;src/ocarina/dsl/invariants/&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#srcocarinadslinvariants&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/invariants/validate.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;validate.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/invariants/assertions.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;assertions.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/invariants/errors.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;errors.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/invariants/internals/validation_chain.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;internals/validation_chain.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;srcocarinadsltesting&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing/&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#srcocarinadsltesting&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/oc_test.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;oc_test.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/oc_test_suite.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;oc_test_suite.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/oc_test_campaign.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;oc_test_campaign.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/oc_test_cycle.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;oc_test_cycle.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/watcher.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;watcher.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/filter_tests_by_ids.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;filter_tests_by_ids.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/internals/test_executor.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;internals/test_executor.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/internals/test_flow.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;internals/test_flow.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/selenium/create_test.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;selenium/create_test.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/selenium/create_watcher.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;selenium/create_watcher.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;srcocarinadsltesting_with_railway&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing_with_railway/&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#srcocarinadsltesting_with_railway&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing_with_railway/chain_actions.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;chain_actions.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing_with_railway/match_page.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;match_page.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing_with_railway/internals/action_chain.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;internals/action_chain.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing_with_railway/constructors/create_act.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;constructors/create_act.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;srcocarinaaggregates&#34;&gt;&lt;code&gt;src/ocarina/aggregates/&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#srcocarinaaggregates&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/aggregates/tests_layers.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;tests_layers.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;srcocarinainfra&#34;&gt;&lt;code&gt;src/ocarina/infra/&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#srcocarinainfra&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/infra/drivers_pool.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;drivers_pool.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/infra/driver_builder.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;driver_builder.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/infra/screenshotter.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;screenshotter.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/infra/act_counter.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;act_counter.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/infra/selenium/create_driver.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;selenium/create_driver.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/infra/selenium/create_drivers_pool.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;selenium/create_drivers_pool.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/infra/selenium/create_screenshotter.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;selenium/create_screenshotter.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/infra/selenium/driver_healthcheck.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;selenium/driver_healthcheck.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/infra/selenium/mixins.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;selenium/mixins.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;srcocarinaports&#34;&gt;&lt;code&gt;src/ocarina/ports/&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#srcocarinaports&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/ports/ilogger.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;ilogger.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/ports/itake_screenshot.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;itake_screenshot.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;srcocarinapom&#34;&gt;&lt;code&gt;src/ocarina/pom/&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#srcocarinapom&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/pom/base.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;base.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;srcocarinaopinionated&#34;&gt;&lt;code&gt;src/ocarina/opinionated/&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#srcocarinaopinionated&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/infra/act_counter.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;infra/act_counter.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/cli/builder.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;cli/builder.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/cli/store.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;cli/store.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/cli/phantoms.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;cli/phantoms.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/cli/selenium/create_cli_store.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;cli/selenium/create_cli_store.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/dsl/drive_page.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;dsl/drive_page.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/launcher/bootstrap.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;launcher/bootstrap.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/loggers/print_logger.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;loggers/print_logger.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/loggers/file_logger.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;loggers/file_logger.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/loggers/utils/format_metadata_str.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;loggers/utils/format_metadata_str.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/plugins/reports/pretty_print_results.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;plugins/reports/pretty_print_results.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/plugins/reports/results_to_json.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;plugins/reports/results_to_json.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/plugins/reports/docx_tests_proofs.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;plugins/reports/docx_tests_proofs.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/plugins/reports/timing.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;plugins/reports/timing.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;tests&#34;&gt;Tests&lt;a class=&#34;anchor&#34; href=&#34;#tests&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/tests/cram/cli_help.t&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;tests/cram/cli_help.t&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/tests/cram/cli_happy_path.t&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;tests/cram/cli_happy_path.t&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;(other &lt;code&gt;.t&lt;/code&gt;s)&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/tests/scenarios/conftest.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;tests/scenarios/conftest.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/tests/scenarios/test_railway_and_action_chain.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;tests/scenarios/test_railway_and_action_chain.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;(other &lt;code&gt;test_*.py&lt;/code&gt;)&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/tests/dsl/invariants/test_types.yml&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;tests/dsl/invariants/test_types.yml&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/tests/dsl/testing_with_railway/test_types.yml&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;tests/dsl/testing_with_railway/test_types.yml&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;workflows&#34;&gt;Workflows&lt;a class=&#34;anchor&#34; href=&#34;#workflows&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/.github/workflows/main_ci.yml&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;.github/workflows/main_ci.yml&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/.github/workflows/dev_ci.yml&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;.github/workflows/dev_ci.yml&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/.github/workflows/unstable_python_full_build.yml&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;.github/workflows/unstable_python_full_build.yml&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/.github/actions/allure-history/action.yml&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;.github/actions/allure-history/action.yml&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;mojo-molotovocarina-example&#34;&gt;&lt;code&gt;mojo-molotov/ocarina-example&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#mojo-molotovocarina-example&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-example/blob/main/src/main.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/main.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-example/blob/main/src/tests/cycles/e2e.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/tests/cycles/e2e.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-example/blob/main/src/tests/scenarios/dashboard/access/happy_paths.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/tests/scenarios/dashboard/access/happy_paths.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-example/blob/main/src/lib/ext/ocarina/adapters/agnostic/act.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/lib/ext/ocarina/adapters/agnostic/act.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-example/blob/main/src/lib/ext/ocarina/adapters/agnostic/env_getters.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/lib/ext/ocarina/adapters/agnostic/env_getters.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-example/blob/main/src/lib/ext/selenium/humanize/proxy.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/lib/ext/selenium/humanize/proxy.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-example/blob/main/src/lib/ext/selenium/watchers/catch_me_if_you_can_watcher.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/lib/ext/selenium/watchers/catch_me_if_you_can_watcher.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-example/blob/main/src/lib/ext/redis/client.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/lib/ext/redis/client.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-example/blob/main/src/caches/l1.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/caches/l1.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-example/blob/main/src/caches/reserve_free_cache_key.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/caches/reserve_free_cache_key.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-example/blob/main/src/api/retrieve_dashboard_otp_code.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/api/retrieve_dashboard_otp_code.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;workflows-1&#34;&gt;Workflows&lt;a class=&#34;anchor&#34; href=&#34;#workflows-1&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-example/blob/main/.github/workflows/main_ci.yml&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;.github/workflows/main_ci.yml&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-example/blob/main/.github/workflows/dev_ci.yml&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;.github/workflows/dev_ci.yml&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-example/blob/main/.github/workflows/e2e.yml&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;.github/workflows/e2e.yml&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;mojo-molotovocarina-with-ai-example&#34;&gt;&lt;code&gt;mojo-molotov/ocarina-with-ai-example&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#mojo-molotovocarina-with-ai-example&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;docs&#34;&gt;Docs&lt;a class=&#34;anchor&#34; href=&#34;#docs&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-with-ai-example/blob/main/README.md&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-with-ai-example/blob/main/CLAUDE.md&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-with-ai-example/blob/main/CURA_FRD.md&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;CURA_FRD.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-with-ai-example/blob/main/CURA_TEST_STRATEGY.md&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;CURA_TEST_STRATEGY.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-with-ai-example/blob/main/IDENTIFIED_GAPS.md&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;IDENTIFIED_GAPS.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;code-1&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code-1&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-with-ai-example/blob/main/src/main.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/main.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-with-ai-example/blob/main/src/tests/cycles/e2e.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/tests/cycles/e2e.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-with-ai-example/blob/main/src/tests/scenarios/_fragments/auth.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/tests/scenarios/_fragments/auth.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-with-ai-example/blob/main/src/tests/scenarios/appointment/past_date_booking.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/tests/scenarios/appointment/past_date_booking.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-with-ai-example/blob/main/src/lib/ext/ocarina/adapters/selenium/test_suite.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/lib/ext/ocarina/adapters/selenium/test_suite.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-with-ai-example/blob/main/src/lib/ext/ocarina/adapters/selenium/create_drivers_pool.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/lib/ext/ocarina/adapters/selenium/create_drivers_pool.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-with-ai-example/blob/main/src/lib/ext/ocarina/adapters/selenium/browser_navigation.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/lib/ext/ocarina/adapters/selenium/browser_navigation.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;workflows-2&#34;&gt;Workflows&lt;a class=&#34;anchor&#34; href=&#34;#workflows-2&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-with-ai-example/blob/main/.github/workflows/ai_proof_ci.yml&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;.github/workflows/ai_proof_ci.yml&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/ocarina-with-ai-example/blob/main/.github/workflows/ai_proof_e2e.yml&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;.github/workflows/ai_proof_e2e.yml&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;mojo-molotovigoristan&#34;&gt;&lt;code&gt;mojo-molotov/igoristan&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#mojo-molotovigoristan&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;code-2&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code-2&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/igoristan/blob/main/src/config/routes.ts&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/config/routes.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/igoristan/blob/main/src/config/brand.ts&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/config/brand.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/igoristan/blob/main/src/hooks/useAuth.ts&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/hooks/useAuth.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/igoristan/blob/main/src/components/LoginForm.tsx&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/components/LoginForm.tsx&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/igoristan/blob/main/src/pages/index/&amp;#43;Page.tsx&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/pages/index/+Page.tsx&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/igoristan/blob/main/src/pages/corsicamon/&amp;#43;Page.tsx&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/pages/corsicamon/+Page.tsx&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/igoristan/blob/main/src/pages/donkey-sausage-eater-detector/&amp;#43;Page.tsx&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/pages/donkey-sausage-eater-detector/+Page.tsx&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/igoristan/blob/main/src/pages/dashboard/&amp;#43;Page.tsx&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/pages/dashboard/+Page.tsx&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/igoristan/blob/main/vite.config.ts&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;vite.config.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/igoristan/blob/main/package.json&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/igoristan/blob/main/eslint.config.ts&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;eslint.config.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;workflows-3&#34;&gt;Workflows&lt;a class=&#34;anchor&#34; href=&#34;#workflows-3&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/igoristan/blob/main/.github/workflows/ci-pr.yml&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;.github/workflows/ci-pr.yml&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/igoristan/blob/main/.github/workflows/deploy.yml&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;mojo-molotovtests-workers&#34;&gt;&lt;code&gt;mojo-molotov/tests-workers&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#mojo-molotovtests-workers&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;code-3&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code-3&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/tests-workers/blob/main/api/otp.ts&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;api/otp.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/tests-workers/blob/main/api/otp-history.ts&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;api/otp-history.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/tests-workers/blob/main/api/corsicadex.ts&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;api/corsicadex.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/tests-workers/blob/main/lib/redis.ts&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;lib/redis.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/tests-workers/blob/main/lib/isAuthorized.ts&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;lib/isAuthorized.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/tests-workers/blob/main/consts/corsicadexData.ts&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;consts/corsicadexData.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/tests-workers/blob/main/package.json&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/tests-workers/blob/main/vercel.json&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;vercel.json&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mojo-molotov/tests-workers/blob/main/README.md&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;(No GitHub Actions workflow.)&lt;/p&gt;</description>
    </item>
    <item>
      <title>00.03 — End-to-end execution flow of an e2e campaign</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/00-big-picture/03-global-execution-flow/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/00-big-picture/03-global-execution-flow/</guid>
      <description>&lt;h1 id=&#34;0003end-to-end-execution-flow-of-an-e2e-campaign&#34;&gt;00.03 — End-to-end execution flow of an e2e campaign&lt;a class=&#34;anchor&#34; href=&#34;#0003end-to-end-execution-flow-of-an-e2e-campaign&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;p&gt;Run &lt;code&gt;python -u src/main.py …&lt;/code&gt; on either suite (&lt;code&gt;ocarina-example&lt;/code&gt; or &lt;code&gt;ocarina-with-ai-example&lt;/code&gt;) and this chain kicks off:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;USER  ────────────────────────────────────────────────────────────────────&#xA;       python -u src/main.py --browser firefox --workers 3 [...]&#xA;              │&#xA;              ▼&#xA;       (1) parse CLI&#xA;           CliStoreSingleton.push(create_selenium_auto_cli_store())&#xA;              │&#xA;              ▼&#xA;       (2) build pool&#xA;           create_selenium_drivers_pool(max_size=N)&#xA;              │&#xA;              ▼&#xA;       (3) warm up external dependencies&#xA;           - Redis (ocarina-example)&#xA;           - Heroku dyno (ai-example, via curl --retry 6)&#xA;              │&#xA;              ▼&#xA;       (4) bootstrap(&#xA;              test_cycle  = create_e2e_test_cycle(drivers_pool),&#xA;              run_plugins = lambda results: run_plugins(&#xA;                                generate_docx_proof, generate_json_results,&#xA;                                exceptions_logger=...),&#xA;              post_exec   = pretty_print_results + sys.exit(1) on fail&#xA;           )&#xA;              │&#xA;OCARINA  ─────┼──────────────────────────────────────────────────────────&#xA;              │&#xA;              ▼&#xA;       (5) TestCycle.run_all (saturate_workers=True)&#xA;              ├─ smoke_tests_campaigns      [mode: fail-fast | wait-for-all]&#xA;              │     └─ TestCampaign.run_all&#xA;              │           └─ TestSuite.run (max_workers, saturate_workers)&#xA;              │                 └─ ThreadPoolExecutor → TestFlow.run&#xA;              │                       └─ pool.acquire() → TestExecutor.execute&#xA;              │                             ├─ setup()                  (optional)&#xA;              │                             ├─ watchers.start()         (daemon threads)&#xA;              │                             ├─ chain_runners            (Railway DSL)&#xA;              │                             ├─ watchers.stop()&#xA;              │                             └─ teardown()               (always)&#xA;              ├─ campaigns (main)&#xA;              │  (skipped if a smoke failed)&#xA;              │&#xA;SUT   ────────┼────────────────────────────────────────────────────────────&#xA;              │&#xA;              ▼&#xA;       Selenium WebDriver ⇄ real browser&#xA;       sometimes: OTP HTTP GET + Redis (on the ocarina-example side)&#xA;              │&#xA;              ▼&#xA;       (6) run_plugins(results)&#xA;              ├─ generate_docx_proof  (reads the log tree, builds .docx files)&#xA;              ├─ generate_json_results (serializes results to .json)&#xA;              ├─ others if declared (parallelized via ThreadPoolExecutor)&#xA;              │&#xA;              ▼&#xA;       (6&amp;#39;) post_exec(results)&#xA;              ├─ pretty_print_results (ANSI, hierarchical)&#xA;              └─ has_test_cycle_failed → sys.exit(1) if applicable&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;step-by-step&#34;&gt;Step-by-step&lt;a class=&#34;anchor&#34; href=&#34;#step-by-step&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;1-parse-cli&#34;&gt;(1) Parse CLI&lt;a class=&#34;anchor&#34; href=&#34;#1-parse-cli&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;create_selenium_auto_cli_store()&lt;/code&gt; detects the OS via &lt;code&gt;platform.system()&lt;/code&gt;:&lt;/p&gt;</description>
    </item>
    <item>
      <title>01.03 — KISS and ostentatious complexity</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/01-philosophy/03-kiss-and-complexity/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/01-philosophy/03-kiss-and-complexity/</guid>
      <description>&lt;h1 id=&#34;0103kiss-and-ostentatious-complexity&#34;&gt;01.03 — KISS and ostentatious complexity&lt;a class=&#34;anchor&#34; href=&#34;#0103kiss-and-ostentatious-complexity&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;the-criticism-that-landed&#34;&gt;The criticism that landed&lt;a class=&#34;anchor&#34; href=&#34;#the-criticism-that-landed&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;First public feedback Ocarina got, &lt;em&gt;systematic&lt;/em&gt; per the author:&lt;/p&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;The first &amp;ldquo;criticisms&amp;rdquo; were often the same: &lt;em&gt;&amp;ldquo;You&amp;rsquo;re bragging about something that&amp;rsquo;s very simple.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;p&gt;The Holy Book takes its time answering. Three pillars: the &lt;strong&gt;operating&lt;/strong&gt; nature of simplicity, the &lt;strong&gt;simple ≠ unsophisticated&lt;/strong&gt; distinction, and the &lt;strong&gt;rejection of every &amp;ldquo;creative&amp;rdquo; counter-proposal&lt;/strong&gt; that came in.&lt;/p&gt;&#xA;&lt;h2 id=&#34;simple--unsophisticated&#34;&gt;Simple ≠ unsophisticated&lt;a class=&#34;anchor&#34; href=&#34;#simple--unsophisticated&#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;This is also why &lt;strong&gt;KISS&lt;/strong&gt; (&lt;em&gt;Keep it simple, stupid&lt;/em&gt;) is so misunderstood in the industry. Many assume &amp;ldquo;simple&amp;rdquo; means &amp;ldquo;unsophisticated.&amp;rdquo; It would be hard to misread a principle more thoroughly.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.03.03 — The failure rail: NeutralAction*</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/03-railway/03-neutral-rail/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/03-railway/03-neutral-rail/</guid>
      <description>&lt;h1 id=&#34;020303the-failure-rail-neutralaction&#34;&gt;02.03.03 — The failure rail: &lt;code&gt;NeutralAction*&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#020303the-failure-rail-neutralaction&#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;Edge case in the builder: what happens when you &lt;em&gt;chain&lt;/em&gt; an action onto an &lt;code&gt;ActionChain&lt;/code&gt; that has already failed?&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;why-neutral-classes-rather-than-a-user-side-if&#34;&gt;Why &lt;em&gt;Neutral&lt;/em&gt; classes rather than a user-side &lt;code&gt;if&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#why-neutral-classes-rather-than-a-user-side-if&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;When you chain an action onto an already-failed &lt;code&gt;ActionChain&lt;/code&gt;, you slide into three &lt;em&gt;Neutral&lt;/em&gt; classes that honor the same API but do nothing.&lt;/p&gt;&#xA;&lt;p&gt;Picture the DSL without this. User code would look like:&lt;/p&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chain &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; act1&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;failure(h)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;success(h)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&#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;if&lt;/span&gt; chain&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;is_ok():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    chain &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; chain&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;then(act2)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;failure(h)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;success(h)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&#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;if&lt;/span&gt; chain&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;is_ok():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    chain &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; chain&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;then(act3)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;failure(h)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;success(h)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;Imperative&lt;/em&gt;, unreadable, and the whole point of the DSL is gone. The neutral rail keeps the writing flat:&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.04.03 — .otherwise(...) and _any_of</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/04-invariants/03-otherwise-any-of/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/04-invariants/03-otherwise-any-of/</guid>
      <description>&lt;h1 id=&#34;020403otherwise-and-_any_of&#34;&gt;02.04.03 — &lt;code&gt;.otherwise(...)&lt;/code&gt; and &lt;code&gt;_any_of&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#020403otherwise-and-_any_of&#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;How Ocarina expresses a &lt;strong&gt;logical OR&lt;/strong&gt; between two predicates without breaking error aggregation.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;a class=&#34;anchor&#34; href=&#34;#the-problem&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Concrete case: you want &amp;ldquo;&lt;code&gt;age == 18&lt;/code&gt; OR &lt;code&gt;age &amp;lt;= 65&lt;/code&gt;.&amp;rdquo; The ROP/invariants syntax is a strict chain of assertions, and every &lt;code&gt;.assert_that()&lt;/code&gt; is an AND. How do you express an OR?&lt;/p&gt;&#xA;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;otherwise&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, fallback: Predicate[T], &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, msg: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; ValidationAssertBlock[T]:&#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; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_last_predicate &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;raise&lt;/span&gt; RuntimeError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;otherwise() must follow assert_that().&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;    fallback_with_msg &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; _with_msg(fallback, msg, &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    combined &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; _any_of(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_last_predicate, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;([&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_otherwise_predicates, fallback_with_msg])&#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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_chain&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_steps&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;pop()                    &lt;span style=&#34;color:#6272a4&#34;&gt;# replace the last step&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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_chain&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;add_assertion(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_value, combined, &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_last_predicate &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; combined&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_otherwise_predicates&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(fallback_with_msg)&#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;return&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Guard&lt;/strong&gt;: &lt;code&gt;otherwise()&lt;/code&gt; has to follow an &lt;code&gt;assert_that()&lt;/code&gt;. With no prior predicate it raises. (In practice the type-checker already rules it out — &lt;code&gt;ValidationStartBlock.otherwise&lt;/code&gt; doesn&amp;rsquo;t exist.)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Combination&lt;/strong&gt;: combine previous predicate + all accumulated &lt;code&gt;otherwise&lt;/code&gt; predicates + the new one, via &lt;code&gt;_any_of&lt;/code&gt;. Result: a composite predicate.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Step replacement&lt;/strong&gt;: &lt;em&gt;pop&lt;/em&gt; the last step from &lt;code&gt;_ValidationChain&lt;/code&gt; and &lt;em&gt;push&lt;/em&gt; the combined one. Otherwise the original predicate AND the combined one would both be present, and the original error would resurface.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;_any_of&#34;&gt;&lt;code&gt;_any_of&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#_any_of&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_any_of&lt;/span&gt;[T](&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;predicates: _PredicateWithMsg[T]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; _PredicateWithMsg[T]:&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_try_predicate&lt;/span&gt;(p: _PredicateWithMsg[T], value: T) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; InvariantViolationError &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;try&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            p(value)&#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;except&lt;/span&gt; InvariantViolationError &lt;span style=&#34;color:#ff79c6&#34;&gt;as&lt;/span&gt; exc:&#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;return&lt;/span&gt; exc&#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;else&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;return&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;combined&lt;/span&gt;(value: T) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        errors &lt;span style=&#34;color:#ff79c6&#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;for&lt;/span&gt; p &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; predicates:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            error &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; _try_predicate(p, value)&#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; error &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;return&lt;/span&gt;                              &lt;span style=&#34;color:#6272a4&#34;&gt;# ✅ one predicate passes → all pass&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            errors&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;append(error)&#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;        formatted_error_messages &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;  | &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;  | &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;join(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;(e) &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; e &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; errors)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        msg &lt;span style=&#34;color:#ff79c6&#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:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;All predicates failed for value &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;value&lt;span style=&#34;color:#f1fa8c&#34;&gt;!r}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&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;» At least one of the following conditions must be satisfied:&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&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;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;formatted_error_messages&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&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:#ff79c6&#34;&gt;raise&lt;/span&gt; InvariantViolationError(msg)&#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;return&lt;/span&gt; _PredicateWithMsg(combined)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;First success wins&lt;/strong&gt;: the moment one predicate passes, &lt;code&gt;combined&lt;/code&gt; returns &lt;code&gt;None&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;All fail&lt;/strong&gt;: build an &lt;em&gt;aggregated&lt;/em&gt; message listing &lt;strong&gt;every&lt;/strong&gt; failure, separated by &lt;code&gt;|&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Result is a &lt;code&gt;_PredicateWithMsg&lt;/code&gt;&lt;/strong&gt;: plug it back into the chain like any other predicate.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Aggregation is recursive&lt;/strong&gt;: 3 successive &lt;code&gt;otherwise()&lt;/code&gt; calls fold &lt;code&gt;(P_orig OR P_otherwise1 OR P_otherwise2 OR P_otherwise3)&lt;/code&gt; into one predicate.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;example&#34;&gt;Example&lt;a class=&#34;anchor&#34; href=&#34;#example&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;validate(age, name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;age&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;.&lt;/span&gt;assert_that(is_equal_to(&lt;span style=&#34;color:#bd93f9&#34;&gt;18&lt;/span&gt;), msg&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Must be 18 or at most 65&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;.&lt;/span&gt;otherwise(is_less_than_or_equal_to(&lt;span style=&#34;color:#bd93f9&#34;&gt;65&lt;/span&gt;), msg&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Must be 18 or at most 65&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;.&lt;/span&gt;execute()&#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;.&lt;/span&gt;raise_if_invalid()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;&#xA;&lt;li&gt;If &lt;code&gt;age == 18&lt;/code&gt;: &lt;code&gt;is_equal_to(18)&lt;/code&gt; passes → OK.&lt;/li&gt;&#xA;&lt;li&gt;If &lt;code&gt;age == 30&lt;/code&gt;: &lt;code&gt;is_equal_to(18)&lt;/code&gt; raises, &lt;code&gt;is_less_than_or_equal_to(65)&lt;/code&gt; passes → OK.&lt;/li&gt;&#xA;&lt;li&gt;If &lt;code&gt;age == 70&lt;/code&gt;: both raise → &lt;code&gt;InvariantViolationError&lt;/code&gt; with:&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;age: All predicates failed for value 70.&#xA;» At least one of the following conditions must be satisfied:&#xA;  | age: Must be 18 or at most 65&#xA;  | age: Must be 18 or at most 65&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(The duplicated message comes from passing the same &lt;code&gt;msg=&lt;/code&gt; twice. Intentional here — we &lt;em&gt;want&lt;/em&gt; a single statement of intent.)&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.05.03 — TestFlow[Driver] — retry policy</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/05-orchestration/03-test-flow-retries/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/05-orchestration/03-test-flow-retries/</guid>
      <description>&lt;h1 id=&#34;020503testflowdriverretry-policy&#34;&gt;02.05.03 — &lt;code&gt;TestFlow[Driver]&lt;/code&gt; — retry policy&lt;a class=&#34;anchor&#34; href=&#34;#020503testflowdriverretry-policy&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/internals/test_flow.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing/internals/test_flow.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;One job&lt;/strong&gt;: for a single &lt;code&gt;Test&lt;/code&gt;, run the retry loop, acquire a fresh driver per attempt, apply the backoff policy.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;why-an-intermediate-level-between-testsuite-and-testexecutor&#34;&gt;Why an intermediate level between &lt;code&gt;TestSuite&lt;/code&gt; and &lt;code&gt;TestExecutor&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#why-an-intermediate-level-between-testsuite-and-testexecutor&#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;Level&lt;/th&gt;&#xA;          &lt;th&gt;Doesn&amp;rsquo;t know about&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;TestExecutor&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Retries, the driver pool&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;TestFlow&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Inter-test concurrency, suite-level aggregation&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestSuite&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;One attempt&amp;rsquo;s execution, retries&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;A &lt;em&gt;genuinely strict&lt;/em&gt; separation. &lt;code&gt;TestFlow&lt;/code&gt; is the only level that knows both the pool and the retry policy — combining them is its whole job.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.10.03 — Screenshotter[TDriver]</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/10-infra/03-screenshotter/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/10-infra/03-screenshotter/</guid>
      <description>&lt;h1 id=&#34;021003screenshottertdriver&#34;&gt;02.10.03 — &lt;code&gt;Screenshotter[TDriver]&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#021003screenshottertdriver&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/infra/screenshotter.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/infra/screenshotter.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Generic, &lt;em&gt;thread-safe&lt;/em&gt; screenshot utility. Driver-agnostic via a &lt;code&gt;Protocol&lt;/code&gt;, configurable via a &lt;code&gt;dataclass&lt;/code&gt;. Supports burst.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;protocol&#34;&gt;&lt;code&gt;Protocol&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#protocol&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;ScreenshotDriver&lt;/span&gt;(Protocol):&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;save_screenshot&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, path: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&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;&amp;#34;&amp;#34;Save standard viewport screenshot.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. &lt;em&gt;Any object&lt;/em&gt; with &lt;code&gt;save_screenshot(path: str) -&amp;gt; bool&lt;/code&gt; qualifies. Selenium WebDriver has it natively (&lt;code&gt;driver.save_screenshot&lt;/code&gt;), Playwright can be wrapped, a fake driver in tests exposes it trivially.&lt;/p&gt;&#xA;&lt;h2 id=&#34;screenshotterconfigtdriver&#34;&gt;&lt;code&gt;ScreenshotterConfig[TDriver]&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#screenshotterconfigtdriver&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@dataclass(frozen&lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;ScreenshotterConfig&lt;/span&gt;[TDriver: ScreenshotDriver]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    output_dir: Path&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    file_ext: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;.png&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    health_check: HealthCheck[TDriver] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    save_full_page: SaveFullPageScreenshot[TDriver] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    default_burst_delay: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;float&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0.5&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    max_filename_retries: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;500&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    uuid_length: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;8&lt;/span&gt;&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;Field&lt;/th&gt;&#xA;          &lt;th&gt;Type&lt;/th&gt;&#xA;          &lt;th&gt;Role&lt;/th&gt;&#xA;          &lt;th&gt;Default&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;output_dir&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Path&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Screenshot directory&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;file_ext&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Extension (&lt;code&gt;.png&lt;/code&gt;, &lt;code&gt;.jpg&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;.png&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;health_check&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;HealthCheck[TDriver] | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;(driver) -&amp;gt; None&lt;/code&gt; that raises if the driver is dead&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;save_full_page&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;SaveFullPageScreenshot[TDriver] | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;(driver, path) -&amp;gt; bool&lt;/code&gt; for full-page screenshots (Firefox only)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;default_burst_delay&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;float&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Delay between 2 shots in burst mode&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;0.5&lt;/code&gt;s&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;max_filename_retries&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Max attempts to generate a unique name&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;500&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;uuid_length&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;UUID suffix length in the name&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;8&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Immutable. Configure once, ship.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.11.03 — Auto CLI store &#43; flags &#43; validation</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/03-selenium-cli/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/03-selenium-cli/</guid>
      <description>&lt;h1 id=&#34;021103auto-cli-store--flags--validation&#34;&gt;02.11.03 — Auto CLI store + flags + validation&lt;a class=&#34;anchor&#34; href=&#34;#021103auto-cli-store--flags--validation&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/cli/selenium/create_cli_store.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/opinionated/cli/selenium/create_cli_store.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;flags&#34;&gt;Flags&lt;a class=&#34;anchor&#34; href=&#34;#flags&#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;Flag&lt;/th&gt;&#xA;          &lt;th&gt;Default&lt;/th&gt;&#xA;          &lt;th&gt;Type&lt;/th&gt;&#xA;          &lt;th&gt;Validation&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;--driver-path&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_file&lt;/code&gt; (except &lt;code&gt;--browser safari&lt;/code&gt;)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--profile-path&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_none&lt;/code&gt; OR &lt;code&gt;is_dir&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--browser&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;None&lt;/code&gt; (required)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;argparse &lt;code&gt;choices&lt;/code&gt; (per OS)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--not-headless&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;False&lt;/code&gt; (= headless by default)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool&lt;/code&gt; (&lt;code&gt;store_true&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;phantom&lt;/em&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--workers&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;5&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;is_positive&lt;/code&gt; + &lt;code&gt;is_not_zero&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--logger&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;&amp;quot;terminal+file&amp;quot;&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;choices=LOGGERS_CHOICES&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--wait-timeout&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;10&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;≤ 60&lt;/code&gt;, &lt;code&gt;&amp;gt; 0&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--dont-force-delete-tmp-dirs&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool&lt;/code&gt; (&lt;code&gt;store_true&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;phantom&lt;/em&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--only&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;list[str]&lt;/code&gt; (&lt;code&gt;nargs=&amp;quot;+&amp;quot;&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;phantom&lt;/em&gt; + mutex&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;--exclude&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;list[str]&lt;/code&gt; (&lt;code&gt;nargs=&amp;quot;+&amp;quot;&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;phantom&lt;/em&gt; + mutex&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_DEFAULT_WORKERS_AMOUNT &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_DEFAULT_LOGGER: SupportedLogger &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;terminal+file&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_DEFAULT_BROWSER_AUTOMATION_TIMEOUT &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;10&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_MAX_BROWSER_AUTOMATION_TIMEOUT &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;60&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;literal&#34;&gt;&lt;code&gt;Literal[...]&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#literal&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; SeleniumCliStoreKeys &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Literal[&#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;driver_path&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;profile_path&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;browser&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;headless&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;workers&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;logger&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;wait_timeout&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;force_delete_tmp_dirs&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;only&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;exclude&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;These keys are the store&amp;rsquo;s whole vocabulary. Call-sites use them: &lt;code&gt;store.get(&amp;quot;workers&amp;quot;)&lt;/code&gt;, &lt;code&gt;store.get(&amp;quot;browser&amp;quot;)&lt;/code&gt;, etc.&lt;/p&gt;</description>
    </item>
    <item>
      <title>03.03 — Lazy evaluation</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/03-lazy-evaluation/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/03-lazy-evaluation/</guid>
      <description>&lt;h1 id=&#34;0303lazy-evaluation&#34;&gt;03.03 — Lazy evaluation&lt;a class=&#34;anchor&#34; href=&#34;#0303lazy-evaluation&#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;In Ocarina, &lt;strong&gt;nothing is executed until you explicitly trigger it&lt;/strong&gt;. That&amp;rsquo;s what makes scenarios composable as values.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;zzz&#34;&gt;Zzz&lt;a class=&#34;anchor&#34; href=&#34;#zzz&#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;Location&lt;/th&gt;&#xA;          &lt;th&gt;Form&lt;/th&gt;&#xA;          &lt;th&gt;Trigger&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;ChainRunner[T]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Thunk[ActionChain[T]]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;runner.run()&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;validate(...)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ValidationStartBlock&lt;/code&gt; / &lt;code&gt;ValidationAssertBlock&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;.execute()&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;match_page(...)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;returns a &lt;code&gt;ChainRunner[Any]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;.run()&lt;/code&gt; (via the surrounding chain)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;Watcher.callback&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Callable[[Watcher], None]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;_loop&lt;/code&gt; when &lt;code&gt;start()&lt;/code&gt; is called&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;logger.set_prefix(thunk)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Thunk[str]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;recomputed at each log call&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;Scenario.setup&lt;/code&gt; / &lt;code&gt;teardown&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Effect&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;called by &lt;code&gt;TestExecutor&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;bootstrap(post_exec=...)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Callable[[TestCycleResults], None]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;called after &lt;code&gt;run_plugins&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;CliBuilder(effects_factory=lambda ns: (...))&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Effects&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;called after the argparse parse&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;test_scenario: TestScenario[Driver]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Callable[[Driver, ILogger], Scenario[Driver]]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;called by &lt;code&gt;Test.spawn&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;dispatch[mode]()&lt;/code&gt; in &lt;code&gt;TestCycle.run_all&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;dict of &lt;code&gt;Thunk[bool]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;called on lookup&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;chainrunner&#34;&gt;&lt;code&gt;ChainRunner&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#chainrunner&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;runner &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; drive_page(act1, act2, act3)        &lt;span style=&#34;color:#6272a4&#34;&gt;# ⚠️  nothing executed&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;# … later …&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chain &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;run()                         &lt;span style=&#34;color:#6272a4&#34;&gt;# ▶︎  execution&lt;/span&gt;&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;Capability&lt;/th&gt;&#xA;          &lt;th&gt;Without laziness&lt;/th&gt;&#xA;          &lt;th&gt;With laziness&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Store a scenario in a variable&lt;/td&gt;&#xA;          &lt;td&gt;impossible (already executed)&lt;/td&gt;&#xA;          &lt;td&gt;trivial&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Multiply &lt;code&gt;[runner] * 5&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;executes once, you get 5 refs to the result&lt;/td&gt;&#xA;          &lt;td&gt;executes 5 times&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Pass a &lt;code&gt;runner&lt;/code&gt; to another &lt;code&gt;runner&lt;/code&gt; (composition)&lt;/td&gt;&#xA;          &lt;td&gt;impossible&lt;/td&gt;&#xA;          &lt;td&gt;trivial&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Reorder &lt;code&gt;act&lt;/code&gt;s in a test refactor&lt;/td&gt;&#xA;          &lt;td&gt;hard&lt;/td&gt;&#xA;          &lt;td&gt;trivial&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;validateexecute&#34;&gt;&lt;code&gt;validate(...).execute()&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#validateexecute&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; validate(value, name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;x&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;assert_that(is_positive)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;assert_that(is_not_zero)&#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;# … you can compose …&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;combined &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; chain_validations(v, other_validation)&#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;# … nothing executed so far …&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;combined&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;raise_if_invalid()        &lt;span style=&#34;color:#6272a4&#34;&gt;# ▶︎  execution + aggregation&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s how &lt;code&gt;_ValidationChain&lt;/code&gt; &lt;strong&gt;collects&lt;/strong&gt; every error before raising a single one (&lt;code&gt;AggregateInvariantViolationError&lt;/code&gt;).&lt;/p&gt;</description>
    </item>
    <item>
      <title>04.03 — Scenario tests (pytest &#43; allure)</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/04-internal-tests/03-pytest-scenarios/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/04-internal-tests/03-pytest-scenarios/</guid>
      <description>&lt;h1 id=&#34;0403scenario-tests-pytest--allure&#34;&gt;04.03 — Scenario tests (pytest + allure)&lt;a class=&#34;anchor&#34; href=&#34;#0403scenario-tests-pytest--allure&#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;15 &lt;code&gt;test_*.py&lt;/code&gt; files in &lt;code&gt;tests/scenarios/&lt;/code&gt; cover the DSL, orchestration and the Playwright actor. Dynamic tests.&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;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;tests/scenarios/&#xA;├── conftest.py                              # FakeDriver, RecordingPOM, make_*&#xA;├── test_railway_and_action_chain.py         # create_act, drive_page, Result, ChainRunner&#xA;├── test_match_page.py                       # match_page / when (first/last branches, exceptions)&#xA;├── test_validate.py                         # validate, assert_that, otherwise, then, chain_validations&#xA;├── test_invariants_properties.py            # hypothesis (see 06-hypothesis-properties)&#xA;├── test_drivers_pool.py                     # WebDriversPool (acquire, warmup, shutdown, semaphore leaks)&#xA;├── test_screenshotter.py                    # Screenshotter (single shot, burst, healthcheck, threadsafety)&#xA;├── test_driver_builder.py                   # DriverBuilder (profile tmp dir, dispose)&#xA;├── test_watcher.py                          # Watcher (start, stop, callback errors swallowed, dedup cache)&#xA;├── test_playwright_driver_actor.py          # PlaywrightDriver actor (sync_playwright mocked, no browser)&#xA;├── test_playwright_adapter.py               # real-browser smoke (skipped if no Chromium installed)&#xA;├── test_test_suite.py                       # TestSuite (parallel, saturation, only/exclude, transient_errors)&#xA;├── test_test_cycle_and_bootstrap.py         # TestCycle, mode fail-fast vs wait-for-all, bootstrap&#xA;├── test_loggers_and_reports.py              # PrintLogger, FileLogger, pretty_print, JSON&#xA;├── test_docx_tests_proofs.py                # generate_docx_proof (parse logs, insert images)&#xA;└── test_env.py                              # smoke test: versions, Python 3.14+&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;example-internal-test&#34;&gt;Example internal test&lt;a class=&#34;anchor&#34; href=&#34;#example-internal-test&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@allure.epic(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Railway / action chain&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@allure.feature(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Action chain&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@allure.tag(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;act&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;handlers&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;happy-path&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@allure.severity(allure&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;severity_level&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;CRITICAL)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@allure.label(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;layer&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;unit&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@allure.title(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;A successful act reports success; the success handler fires once&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;test_success_act_fires_success_handler&lt;/span&gt;() &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pom &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; RecordingPOM()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    calls &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;success&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;failure&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;0&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;    chain &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        create_act(pom, &lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt; p: p&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;step(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;click&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;.&lt;/span&gt;failure(&lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt; _: calls&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#50fa7b&#34;&gt;__setitem__&lt;/span&gt;(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;failure&amp;#34;&lt;/span&gt;, calls[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;failure&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&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;.&lt;/span&gt;success(&lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt;: calls&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#50fa7b&#34;&gt;__setitem__&lt;/span&gt;(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;success&amp;#34;&lt;/span&gt;, calls[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;success&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&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;.&lt;/span&gt;execute()&#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;&#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;assert&lt;/span&gt; chain&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;is_ok()&#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;assert&lt;/span&gt; pom&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;calls &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;click&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;assert&lt;/span&gt; calls &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; {&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;success&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;failure&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;}&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;Assertion&lt;/th&gt;&#xA;          &lt;th&gt;Target&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;chain.is_ok()&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Public API&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;pom.calls == [&amp;quot;click&amp;quot;]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Observable POM behavior (recording)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;calls == {&amp;quot;success&amp;quot;: 1, &amp;quot;failure&amp;quot;: 0}&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Right handler called&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;allure-annotations&#34;&gt;Allure annotations&lt;a class=&#34;anchor&#34; href=&#34;#allure-annotations&#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;Category&lt;/th&gt;&#xA;          &lt;th&gt;Typical values&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;epic&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;Railway / action chain&amp;rdquo;, &amp;ldquo;Invariants&amp;rdquo;, &amp;ldquo;Orchestration&amp;rdquo;, &amp;ldquo;Watcher&amp;rdquo;, &amp;ldquo;Drivers pool&amp;rdquo;, &amp;ldquo;Reports&amp;rdquo;, &amp;ldquo;Match page&amp;rdquo;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;feature&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;Action chain&amp;rdquo;, &amp;ldquo;Validate&amp;rdquo;, &amp;ldquo;TestSuite&amp;rdquo;, &amp;ldquo;TestCycle&amp;rdquo;, &amp;ldquo;Bootstrap&amp;rdquo;, &amp;ldquo;Pretty print&amp;rdquo;, &amp;ldquo;JSON&amp;rdquo;, &amp;ldquo;DOCX proofs&amp;rdquo;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;tag&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;act&amp;rdquo;, &amp;ldquo;handlers&amp;rdquo;, &amp;ldquo;happy-path&amp;rdquo;, &amp;ldquo;error-handling&amp;rdquo;, &amp;ldquo;retry&amp;rdquo;, &amp;ldquo;transient&amp;rdquo;, &amp;ldquo;smoke&amp;rdquo;, &amp;ldquo;skip&amp;rdquo;, &amp;ldquo;saturation&amp;rdquo;, &amp;ldquo;parallel&amp;rdquo;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;severity&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;CRITICAL &amp;gt; NORMAL &amp;gt; MINOR &amp;gt; TRIVIAL&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;label(&amp;quot;layer&amp;quot;, ...)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;unit&amp;rdquo;, &amp;ldquo;integration&amp;rdquo;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;test_test_suitepy&#34;&gt;&lt;code&gt;test_test_suite.py&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#test_test_suitepy&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Tests pass in sequential mode (&lt;code&gt;max_workers=1&lt;/code&gt;).&lt;/li&gt;&#xA;&lt;li&gt;Tests pass in parallel mode (&lt;code&gt;max_workers=N&lt;/code&gt;).&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;--only&lt;/code&gt; / &lt;code&gt;--exclude&lt;/code&gt; filtering.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;--only&lt;/code&gt; + &lt;code&gt;--exclude&lt;/code&gt; mutex.&lt;/li&gt;&#xA;&lt;li&gt;Saturation: tests cloned with &lt;code&gt;[COPY N]&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Retries on transient errors.&lt;/li&gt;&#xA;&lt;li&gt;Setup failure → SKIPPED when every attempt fails at setup.&lt;/li&gt;&#xA;&lt;li&gt;A &lt;code&gt;skipped=True&lt;/code&gt; test stops immediately.&lt;/li&gt;&#xA;&lt;li&gt;Guardrails (unique names, unique IDs).&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;take_screenshot&lt;/code&gt; called on fail if &lt;code&gt;autoscreen_on_fail&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Exact attempt counting on &lt;code&gt;max_retries&lt;/code&gt; (one test covers the loop edge).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;test_match_pagepy&#34;&gt;&lt;code&gt;test_match_page.py&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#test_match_pagepy&#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;Case&lt;/th&gt;&#xA;          &lt;th&gt;Assertion&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;First branch matches → executed&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;chain.is_ok()&lt;/code&gt;, later branches not evaluated&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Second branch matches → executed&lt;/td&gt;&#xA;          &lt;td&gt;id.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;No branch matches&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;NoMatchingBranchError&lt;/code&gt; in the &lt;code&gt;Fail&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Branch matches, but its &lt;code&gt;then&lt;/code&gt; fails&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Fail&lt;/code&gt; propagated&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;condition&lt;/code&gt; raises an ordinary &lt;code&gt;Exception&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Treated as &lt;code&gt;False&lt;/code&gt;, later branches tried&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;condition&lt;/code&gt; raises an exception from the &lt;code&gt;raised_exceptions&lt;/code&gt; tuple&lt;/td&gt;&#xA;          &lt;td&gt;Re-raise&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Branch &lt;code&gt;then=[]&lt;/code&gt; matches&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Ok(None)&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;match_page&lt;/code&gt; in the middle of a &lt;code&gt;test_chain&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Short-circuits if failed&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;test_drivers_poolpy&#34;&gt;&lt;code&gt;test_drivers_pool.py&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#test_drivers_poolpy&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Uses a &lt;code&gt;FakeDriverFactory&lt;/code&gt; that can be configured to raise / block / sleep, and counts its calls.&lt;/p&gt;</description>
    </item>
    <item>
      <title>05.03 — The fake useAuth &#43; MFA OTP</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/05-igoristan/03-use-auth/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/05-igoristan/03-use-auth/</guid>
      <description>&lt;h1 id=&#34;0503the-fake-useauth--mfa-otp&#34;&gt;05.03 — The fake &lt;code&gt;useAuth&lt;/code&gt; + MFA OTP&lt;a class=&#34;anchor&#34; href=&#34;#0503the-fake-useauth--mfa-otp&#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;Igoristan&amp;rsquo;s &lt;code&gt;useAuth&lt;/code&gt; hook is deliberately fake. Two mechanics to exercise Ocarina&amp;rsquo;s replay and worker coordination: a 10% random failure and an optional MFA OTP.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;// src/hooks/useAuth.ts&#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;const&lt;/span&gt; VALID_PASSWORD &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;figatellu&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;const&lt;/span&gt; NOT_AUTHENTICATED &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&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;const&lt;/span&gt; AUTHENTICATED_WITHOUT_MFA &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&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;const&lt;/span&gt; AUTHENTICATED_WITH_MFA &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;2&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;// * ... This is FAKED&#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;export&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt; useAuth &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; () &lt;span style=&#34;color:#ff79c6&#34;&gt;=&amp;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;const&lt;/span&gt; [_isAuthenticated, setIsAuthenticated] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; useLocalStorage(&#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;isAuthenticated&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    NOT_AUTHENTICATED,&#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;const&lt;/span&gt; [isLoading, setIsLoading] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; useState(&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;&#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;const&lt;/span&gt; isAuthenticated &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; useCallback(&#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;=&amp;gt;&lt;/span&gt; _isAuthenticated &lt;span style=&#34;color:#ff79c6&#34;&gt;!==&lt;/span&gt; NOT_AUTHENTICATED,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [_isAuthenticated],&#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;const&lt;/span&gt; isAuthenticatedWithMFA &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; useCallback(&#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;=&amp;gt;&lt;/span&gt; _isAuthenticated &lt;span style=&#34;color:#ff79c6&#34;&gt;===&lt;/span&gt; AUTHENTICATED_WITH_MFA,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [_isAuthenticated],&#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;const&lt;/span&gt; logout &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; useCallback(&#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;=&amp;gt;&lt;/span&gt; setIsAuthenticated(NOT_AUTHENTICATED),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [setIsAuthenticated],&#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;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  useEffect(() &lt;span style=&#34;color:#ff79c6&#34;&gt;=&amp;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;const&lt;/span&gt; delay &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;500&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; randint(&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;); &lt;span style=&#34;color:#6272a4&#34;&gt;// ◄── random delay 0.5–2.5s&#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;const&lt;/span&gt; timer &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; setTimeout(() &lt;span style=&#34;color:#ff79c6&#34;&gt;=&amp;gt;&lt;/span&gt; setIsLoading(&lt;span style=&#34;color:#ff79c6&#34;&gt;false&lt;/span&gt;), delay);&#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;return&lt;/span&gt; () &lt;span style=&#34;color:#ff79c6&#34;&gt;=&amp;gt;&lt;/span&gt; clearTimeout(timer);&#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;&#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;const&lt;/span&gt; login: &lt;span style=&#34;color:#8be9fd&#34;&gt;LoginFn&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; ({ pre &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;false&lt;/span&gt;, password, withMFA }) &lt;span style=&#34;color:#ff79c6&#34;&gt;=&amp;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;if&lt;/span&gt; (password &lt;span style=&#34;color:#ff79c6&#34;&gt;===&lt;/span&gt; VALID_PASSWORD &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;Math&lt;/span&gt;.random() &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0.9&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;// ◄── 10% fail despite correct password&#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;if&lt;/span&gt; (&lt;span style=&#34;color:#ff79c6&#34;&gt;!&lt;/span&gt;pre) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        setIsAuthenticated(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          withMFA &lt;span style=&#34;color:#ff79c6&#34;&gt;?&lt;/span&gt; AUTHENTICATED_WITH_MFA : &lt;span style=&#34;color:#8be9fd&#34;&gt;AUTHENTICATED_WITHOUT_MFA&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;      }&#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;return&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;    }&#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;return&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;  };&#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;return&lt;/span&gt; { isAuthenticatedWithMFA, isAuthenticated, isLoading, logout, login };&#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;h2 id=&#34;mechanics&#34;&gt;Mechanics&lt;a class=&#34;anchor&#34; href=&#34;#mechanics&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;1-localstorage&#34;&gt;1. &lt;code&gt;LocalStorage&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#1-localstorage&#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-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt; [_isAuthenticated, setIsAuthenticated] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; useLocalStorage(&#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;isAuthenticated&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  NOT_AUTHENTICATED,&#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;Three values: &lt;code&gt;-1&lt;/code&gt; (not authenticated), &lt;code&gt;1&lt;/code&gt; (no MFA), &lt;code&gt;2&lt;/code&gt; (with MFA).&lt;br&gt;&#xA;Persisted in &lt;code&gt;localStorage&lt;/code&gt;, easy to inspect for testing / manual debugging.&lt;/p&gt;</description>
    </item>
    <item>
      <title>06.03 — GET /api/otp-history</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/06-tests-workers/03-otp-history/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/06-tests-workers/03-otp-history/</guid>
      <description>&lt;h1 id=&#34;0603get-apiotp-history&#34;&gt;06.03 — &lt;code&gt;GET /api/otp-history&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#0603get-apiotp-history&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/tests-workers/blob/main/api/otp-history.ts&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;api/otp-history.ts&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;type&lt;/span&gt; { NextRequest } &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;next/server&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;import&lt;/span&gt; redis &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;../lib/redis&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;import&lt;/span&gt; isAuthorized &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;../lib/isAuthorized&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:#ff79c6&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt; config &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; { runtime&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;edge&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:#ff79c6&#34;&gt;const&lt;/span&gt; corsHeaders &lt;span style=&#34;color:#ff79c6&#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:#f1fa8c&#34;&gt;&amp;#34;Access-Control-Allow-Origin&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&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;Access-Control-Allow-Methods&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;GET, OPTIONS&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;Access-Control-Allow-Headers&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;x-api-key, Content-Type&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;as&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;const&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;export&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;function&lt;/span&gt; handler(request: &lt;span style=&#34;color:#8be9fd&#34;&gt;NextRequest&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;if&lt;/span&gt; (request.method &lt;span style=&#34;color:#ff79c6&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;OPTIONS&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;return&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Response(&lt;span style=&#34;color:#ff79c6&#34;&gt;null&lt;/span&gt;, { status: &lt;span style=&#34;color:#8be9fd&#34;&gt;200&lt;/span&gt;, headers: &lt;span style=&#34;color:#8be9fd&#34;&gt;corsHeaders&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;&#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; (&lt;span style=&#34;color:#ff79c6&#34;&gt;!&lt;/span&gt;isAuthorized(request)) {&#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;return&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Response(JSON.stringify({ error&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Unauthorized&amp;#34;&lt;/span&gt; }), {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      status: &lt;span style=&#34;color:#8be9fd&#34;&gt;401&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      headers&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; { &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;, ...corsHeaders },&#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;  }&#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:#8be9fd;font-style:italic&#34;&gt;let&lt;/span&gt; cursor &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;0&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;const&lt;/span&gt; allEvents &lt;span style=&#34;color:#ff79c6&#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;const&lt;/span&gt; BATCH_SIZE &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;100&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;do&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;const&lt;/span&gt; [newCursor, keys] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;await&lt;/span&gt; redis.scan(cursor, {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      match&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;otp:*&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      count: &lt;span style=&#34;color:#8be9fd&#34;&gt;BATCH_SIZE&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;    cursor &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; newCursor;&#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;if&lt;/span&gt; (keys.length &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&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;const&lt;/span&gt; values &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;await&lt;/span&gt; redis.mget(...keys);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      allEvents.push(...values.filter(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;Boolean&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;while&lt;/span&gt; (cursor &lt;span style=&#34;color:#ff79c6&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;0&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:#ff79c6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Response(JSON.stringify(allEvents), {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    headers&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; { &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;, ...corsHeaders },&#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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;redis-scan-mechanics&#34;&gt;Redis &lt;code&gt;SCAN&lt;/code&gt; mechanics&lt;a class=&#34;anchor&#34; href=&#34;#redis-scan-mechanics&#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-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;let&lt;/span&gt; cursor &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;0&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;const&lt;/span&gt; allEvents &lt;span style=&#34;color:#ff79c6&#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;const&lt;/span&gt; BATCH_SIZE &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;100&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;do&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;const&lt;/span&gt; [newCursor, keys] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;await&lt;/span&gt; redis.scan(cursor, {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    match&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;otp:*&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    count: &lt;span style=&#34;color:#8be9fd&#34;&gt;BATCH_SIZE&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;  cursor &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; newCursor;&#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;if&lt;/span&gt; (keys.length &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&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;const&lt;/span&gt; values &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;await&lt;/span&gt; redis.mget(...keys);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    allEvents.push(...values.filter(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;Boolean&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;while&lt;/span&gt; (cursor &lt;span style=&#34;color:#ff79c6&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;code&gt;cursor = &amp;quot;0&amp;quot;&lt;/code&gt; (initialization).&lt;/li&gt;&#xA;&lt;li&gt;Loop:&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;SCAN &amp;lt;cursor&amp;gt; MATCH otp:* COUNT 100&lt;/code&gt; returns &lt;code&gt;[newCursor, keys]&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;If &lt;code&gt;keys&lt;/code&gt; non-empty: &lt;code&gt;MGET k1 k2 ...&lt;/code&gt; fetches the values.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;cursor = newCursor&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;We stop when &lt;code&gt;cursor === &amp;quot;0&amp;quot;&lt;/code&gt; (cycle complete).&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Why &lt;code&gt;SCAN&lt;/code&gt; rather than &lt;code&gt;KEYS&lt;/code&gt;?&lt;/p&gt;</description>
    </item>
    <item>
      <title>07.03 — Dashboard login scenarios</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/03-scenarios-login/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/03-scenarios-login/</guid>
      <description>&lt;h1 id=&#34;0703dashboard-login-scenarios&#34;&gt;07.03 — Dashboard login scenarios&lt;a class=&#34;anchor&#34; href=&#34;#0703dashboard-login-scenarios&#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 families: happy paths, unhappy paths, data-driven multi-login. All exercise the 10%-fail &lt;code&gt;useAuth&lt;/code&gt; and OTP coordination.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;dashboard_login&#34;&gt;&lt;code&gt;dashboard_login&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#dashboard_login&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/tests/campaigns/dashboard_login.py&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;create_igoristan_login_campaign&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, drivers_pool: SeleniumWebDriversPool) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; TestCampaign:&#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;return&lt;/span&gt; TestCampaign(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Dashboard login&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        suites&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;[&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            create_igoristan_login_happy_paths_test_suite(drivers_pool&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;drivers_pool),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            create_igoristan_login_unhappy_paths_test_suite(drivers_pool&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;drivers_pool),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            create_igoristan_login_data_driven_test_suite(drivers_pool&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;drivers_pool),&#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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;happy-paths&#34;&gt;Happy paths&lt;a class=&#34;anchor&#34; href=&#34;#happy-paths&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/tests/suites/dashboard/access/happy_paths.py&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;create_igoristan_login_happy_paths_test_suite&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, drivers_pool) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; TestSuite:&#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;return&lt;/span&gt; TestSuite(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Login happy paths&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        tests&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;[&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            test_dashboard_login_without_otp_happy_path,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            test_dashboard_login_with_otp_happy_path,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            test_dashboard_login_page_back_to_igoristan_button,&#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;        drivers_pool&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;drivers_pool,&#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;h3 id=&#34;test-1-login-without-otp&#34;&gt;Test 1: login without OTP&lt;a class=&#34;anchor&#34; href=&#34;#test-1-login-without-otp&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;dashboard_login_without_otp_happy_path&lt;/span&gt;(driver, logger):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dashboard_creds &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; create_env_getters()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;get_credentials(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;dashboard&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    on_dashboard_login_page &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; DashboardLoginPage(driver&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;driver)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    on_dashboard_welcome_page &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; DashboardWelcomePage(driver&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;driver)&#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;    just_log_error &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; create_just_log_error(logger&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;logger)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    log_error_with_current_url &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; create_log_error_with_current_url(logger&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;logger, driver&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;driver)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    just_log_success &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; create_just_log_success(logger&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;logger)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    log_success_with_current_url_and_take_screenshot &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; create_log_success_with_current_url_and_take_screenshot(&lt;span style=&#34;color:#ff79c6&#34;&gt;...&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;    retries_amount &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;max&lt;/span&gt;(get_max_workers(), &lt;span style=&#34;color:#bd93f9&#34;&gt;10&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;return&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        drive_page(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(on_dashboard_login_page, open_dashboard_login_page)&#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;.&lt;/span&gt;failure(just_log_error(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Failed to open the dashboard login page...&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;.&lt;/span&gt;success(just_log_success(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Opened the dashboard login page!&amp;#34;&lt;/span&gt;)),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(on_dashboard_login_page, verify_dashboard_login_page)&#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;.&lt;/span&gt;failure(log_error_with_current_url(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Failed to verify...&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;.&lt;/span&gt;success(log_success_with_current_url_and_take_screenshot(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Verified!&amp;#34;&lt;/span&gt;)),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(on_dashboard_login_page,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                login_without_otp_and_with_retries(dashboard_creds, retries_amount, logger&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;logger))&#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;.&lt;/span&gt;failure(just_log_error(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Failed to connect to the dashboard without OTP...&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;.&lt;/span&gt;success(just_log_success(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Connected to the dashboard!&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;        drive_page(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(on_dashboard_welcome_page, verify_dashboard_welcome_page)&#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;.&lt;/span&gt;failure(log_error_with_current_url(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Failed to verify...&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;.&lt;/span&gt;success(log_success_with_current_url_and_take_screenshot(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Verified!&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;retries_amount = max(get_max_workers(), 10)&lt;/code&gt;&lt;/strong&gt;: at least 10 internal login retries to beat the 10% random failure.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;login_without_otp_and_with_retries(creds, retries_amount, logger=logger)&lt;/code&gt;&lt;/strong&gt;: closure capturing creds, retry count, and logger. Returns &lt;code&gt;Callable[[DashboardLoginPage], DashboardLoginPage]&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;2 &lt;code&gt;drive_page&lt;/code&gt;s&lt;/strong&gt;: login page then welcome page. Each opens, verifies, screenshots.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Log factories everywhere&lt;/strong&gt;: no inline lambdas.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;SeleniumTitleMixin&lt;/code&gt;&lt;/strong&gt;: &lt;code&gt;get_current_title()&lt;/code&gt; feeds &lt;code&gt;act&lt;/code&gt;&amp;rsquo;s &lt;code&gt;on_failure&lt;/code&gt; hook.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;test-2-login-with-otp&#34;&gt;Test 2: login with OTP&lt;a class=&#34;anchor&#34; href=&#34;#test-2-login-with-otp&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;dashboard_login_with_otp_happy_path&lt;/span&gt;(driver, logger):&#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;# ... same setup ...&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;    cache &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; in_memory_cache_with_30m_ttl&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fresh_cache_key_for_username &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; reserve_free_cache_key(cache)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fresh_cache_key_for_otp_send_button_click_date &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; reserve_free_cache_key(cache)&#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;return&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        drive_page(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(on_dashboard_login_page, open_dashboard_login_page)&lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(on_dashboard_login_page, verify_dashboard_login_page)&lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(on_dashboard_login_page,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                start_to_login_with_otp_and_with_retries(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    dashboard_creds, retries_amount,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    cache&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;cache, logger&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;logger,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    username_cache_key&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;fresh_cache_key_for_username,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    otp_send_button_click_date_cache_key&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;fresh_cache_key_for_otp_send_button_click_date,&#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;...&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(on_dashboard_login_page, verify_otp_screen)&lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(on_dashboard_login_page,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                type_otp_with_retries(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    retries_amount,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    cache&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;cache, logger&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;logger,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    username_cache_key&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;fresh_cache_key_for_username,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    otp_send_button_click_date_cache_key&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;fresh_cache_key_for_otp_send_button_click_date,&#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;...&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;        drive_page(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(on_dashboard_welcome_page, verify_dashboard_welcome_page)&lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(on_dashboard_welcome_page, click_on_go_to_nested_page_btn)&lt;span style=&#34;color:#ff79c6&#34;&gt;...&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;        drive_page(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(on_dashboard_protected_page, verify_dashboard_protected_page)&lt;span style=&#34;color:#ff79c6&#34;&gt;...&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Three &lt;code&gt;drive_page&lt;/code&gt;s, five &lt;code&gt;act&lt;/code&gt;s in the first. L1 cache + reserved keys share values between acts (see &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/08-caches-locks/&#34;&gt;&lt;code&gt;08-caches-locks.md&lt;/code&gt;&lt;/a&gt;).&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>09.03.03 — Black-hat skills</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/03-black-hat/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/03-black-hat/</guid>
      <description>&lt;h1 id=&#34;090303black-hat-skills-6&#34;&gt;09.03.03 — Black-hat skills (6)&lt;a class=&#34;anchor&#34; href=&#34;#090303black-hat-skills-6&#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;&lt;strong&gt;Ideations&lt;/strong&gt; of business logic vulnerabilities — no execution: &amp;ldquo;&lt;em&gt;security testing is functional and static, never active&lt;/em&gt;&amp;rdquo;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;listing-potentially-non-exhaustive&#34;&gt;Listing (potentially non-exhaustive)&lt;a class=&#34;anchor&#34; href=&#34;#listing-potentially-non-exhaustive&#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;Skill&lt;/th&gt;&#xA;          &lt;th&gt;Target&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;business-logic-vulnerability-ideation&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Break the product through legitimate-looking but malicious usage paths&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;incoherence-attack-ideation&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Each step taken alone looks innocent; but combinations of these steps can cause an inconsistency in the system&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;persistence-attack-ideation&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Repeated attempts at a blocked action&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;permission-appropriateness-audit&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Is the access model itself appropriate?&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;bfcache-exposure-ideation&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;BFCache attacks&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;lateral-resource-ideation&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;IDOR via the address bar only&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&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;p&gt;All these skills &lt;em&gt;imagine&lt;/em&gt; attacks &lt;strong&gt;the AI&lt;/strong&gt; could phrase.&lt;/p&gt;</description>
    </item>
    <item>
      <title>10.03 — ocarina-example workflows</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/03-example-workflows/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/03-example-workflows/</guid>
      <description>&lt;h1 id=&#34;1003ocarina-example-workflows&#34;&gt;10.03 — &lt;code&gt;ocarina-example&lt;/code&gt; workflows&lt;a class=&#34;anchor&#34; href=&#34;#1003ocarina-example-workflows&#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;See also &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/11-ci/&#34;&gt;&lt;code&gt;../07-ocarina-example/11-ci.md&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;a class=&#34;anchor&#34; href=&#34;#overview&#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;Workflow&lt;/th&gt;&#xA;          &lt;th&gt;Trigger&lt;/th&gt;&#xA;          &lt;th&gt;OS&lt;/th&gt;&#xA;          &lt;th&gt;Effect&lt;/th&gt;&#xA;          &lt;th&gt;Note&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;main_ci.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;push/PR main, dispatch&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu + windows × py 3.14&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;make check-coding-style&lt;/code&gt; (mypy + ruff).&lt;/td&gt;&#xA;          &lt;td&gt;Fast PR gate&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;dev_ci.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;push dev, dispatch&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu × py 3.14&lt;/td&gt;&#xA;          &lt;td&gt;Same&lt;/td&gt;&#xA;          &lt;td&gt;For dev branches&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;e2e.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;workflow_dispatch&lt;/code&gt; (env &lt;code&gt;OC&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu + Redis service + Firefox + geckodriver 0.35.0&lt;/td&gt;&#xA;          &lt;td&gt;Full suite run + upload &lt;code&gt;.screenshots/&lt;/code&gt; &lt;code&gt;.ocarina_logs/&lt;/code&gt; &lt;code&gt;.reports/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Manual only&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;vs-ocarinamain_ciyml&#34;&gt;vs &lt;code&gt;ocarina/main_ci.yml&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#vs-ocarinamain_ciyml&#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;Aspect&lt;/th&gt;&#xA;          &lt;th&gt;&lt;code&gt;ocarina/main_ci.yml&lt;/code&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;code&gt;ocarina-example/main_ci.yml&lt;/code&gt;&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;Unit tests&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Yes (pytest + cram)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Allure history&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Yes&lt;/td&gt;&#xA;          &lt;td&gt;No&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;GitHub Pages deploy&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Yes (Allure report)&lt;/td&gt;&#xA;          &lt;td&gt;No&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;OS matrix&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Yes (ubuntu + windows)&lt;/td&gt;&#xA;          &lt;td&gt;Yes (same)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Python matrix&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Optional (3.14 or 3.14+3.15)&lt;/td&gt;&#xA;          &lt;td&gt;No (3.14 only)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&lt;code&gt;ocarina&lt;/code&gt; tests itself (unit tests) in CI. &lt;code&gt;ocarina-example&lt;/code&gt; only runs static checks in CI; its e2e tests are fired on demand via &lt;code&gt;e2e.yml&lt;/code&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.03 — Explicit refusals</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/11-independence/03-explicit-refusals/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/11-independence/03-explicit-refusals/</guid>
      <description>&lt;h1 id=&#34;1103explicit-refusals&#34;&gt;11.03 — Explicit refusals&lt;a class=&#34;anchor&#34; href=&#34;#1103explicit-refusals&#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;Synthesis of refusals scattered across the Holy Book.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;summary-table&#34;&gt;Summary table&lt;a class=&#34;anchor&#34; href=&#34;#summary-table&#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;Refusal&lt;/th&gt;&#xA;          &lt;th&gt;Reason&lt;/th&gt;&#xA;          &lt;th&gt;Reference&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;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Selenium synchronous, Requests synchronous; readability; no point introducing an &lt;em&gt;event-loop&lt;/em&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Holy Book&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;pytest plugin&lt;/td&gt;&#xA;          &lt;td&gt;Independence, ISTQB fidelity&lt;/td&gt;&#xA;          &lt;td&gt;Ocarina &lt;code&gt;README&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Text DSL (Robot, Gherkin)&lt;/td&gt;&#xA;          &lt;td&gt;No translation layer&lt;/td&gt;&#xA;          &lt;td&gt;Holy Book&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Reactive programming&lt;/td&gt;&#xA;          &lt;td&gt;Static scenarios, in-memory cache suffices&lt;/td&gt;&#xA;          &lt;td&gt;Holy Book&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Tricks and hacks&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;Teach the pattern, not the symptom&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; ai-example&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;Stylish&lt;/em&gt;&amp;rdquo; contributions&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;It&amp;rsquo;s my car&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;          &lt;td&gt;Holy Book&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Active security tests&lt;/td&gt;&#xA;          &lt;td&gt;Functional and static only, never attacks&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; ai-example&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;1-refusal-of-asyncawait&#34;&gt;1. Refusal of &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#1-refusal-of-asyncawait&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Central quote:&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.03 — YTCracker, Nerdcore, Digital Gangster</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/03-ytcracker-nerdcore-digital-gangster/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/03-ytcracker-nerdcore-digital-gangster/</guid>
      <description>&lt;h1 id=&#34;1203ytcracker-nerdcore-digital-gangster&#34;&gt;12.03 — YTCracker, Nerdcore, Digital Gangster&lt;a class=&#34;anchor&#34; href=&#34;#1203ytcracker-nerdcore-digital-gangster&#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;Bryce Case Jr., a single man, connects &lt;em&gt;four universes&lt;/em&gt;: the US hacker scene (1999-2005), the birth of &lt;em&gt;Nerdcore&lt;/em&gt; (nerd hip-hop, 2005-2008), Digital Gangster (forum 2005-2017), and crypto culture (from 2013 onward, notably with &lt;em&gt;Bitcoin Baron&lt;/em&gt;). The Holy Book quotes him several times.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;1-identity&#34;&gt;1. Identity&lt;a class=&#34;anchor&#34; href=&#34;#1-identity&#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;Field&lt;/th&gt;&#xA;          &lt;th&gt;Value&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Name&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Bryce Case Jr.&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Born&lt;/td&gt;&#xA;          &lt;td&gt;August 23, 1982, La Mirada, California&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Alias&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;YTCracker&lt;/strong&gt; (pronounced &amp;ldquo;&lt;em&gt;whitey cracker&lt;/em&gt;&amp;rdquo;)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Status&lt;/td&gt;&#xA;          &lt;td&gt;Formerly black hat, now white hat (cybersec consultant + musician)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;The pseudo &amp;ldquo;&lt;em&gt;YTCracker&lt;/em&gt;&amp;rdquo; is pronounced &lt;em&gt;whitey cracker&lt;/em&gt;. It combines &amp;ldquo;&lt;em&gt;Yours Truly&lt;/em&gt;&amp;rdquo;, the name of a character in Neal Stephenson&amp;rsquo;s cyberpunk novel &lt;em&gt;Snow Crash&lt;/em&gt;, and the term &lt;em&gt;cracker&lt;/em&gt; — the one who breaks into computer systems. Bryce Case himself explained this etymology in several interviews.&lt;/p&gt;</description>
    </item>
    <item>
      <title>00.04 — Relations between the six repositories</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/00-big-picture/04-repo-relations/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/00-big-picture/04-repo-relations/</guid>
      <description>&lt;h1 id=&#34;0004relations-between-the-six-repositories&#34;&gt;00.04 — Relations between the six repositories&lt;a class=&#34;anchor&#34; href=&#34;#0004relations-between-the-six-repositories&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;p&gt;Every edge of the graph is a &lt;em&gt;contract&lt;/em&gt; — a secret, a URL, a payload type, or a version. One pair per section below.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ocarina--ocarina-example&#34;&gt;&lt;code&gt;ocarina&lt;/code&gt; ↔ &lt;code&gt;ocarina-example&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#ocarina--ocarina-example&#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;Direction&lt;/th&gt;&#xA;          &lt;th&gt;Mechanism&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;ocarina&lt;/code&gt; → &lt;code&gt;ocarina-example&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;pip install ocarina&lt;/code&gt; from PyPI&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina-example&lt;/code&gt; → &lt;code&gt;ocarina&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;None (the example pushes nothing)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;The example &lt;strong&gt;writes its own adapters&lt;/strong&gt; on top of the framework:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;lib/ext/ocarina/adapters/agnostic/act.py&lt;/code&gt; → adds an &lt;code&gt;on_failure&lt;/code&gt; hook that turns an HTTP error page (title matched by &lt;code&gt;ERROR_PAGE_REGEX&lt;/code&gt;) into &lt;code&gt;HttpErrorPageReachedError&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;lib/ext/ocarina/adapters/agnostic/match_page.py&lt;/code&gt; → &lt;code&gt;create_match_page(raised_exceptions=transient_errors)&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;lib/ext/ocarina/adapters/agnostic/env_getters.py&lt;/code&gt; → typed &lt;code&gt;EnvGetters[_CredsKeys, _ValuesKeys]&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;lib/ext/ocarina/adapters/selenium/test_suite.py&lt;/code&gt; → pins &lt;code&gt;max_retries_per_test=8&lt;/code&gt;, &lt;code&gt;transient_errors=...&lt;/code&gt;, &lt;code&gt;autoscreen_on_fail=True&lt;/code&gt;, propagates &lt;code&gt;--only&lt;/code&gt;/&lt;code&gt;--exclude&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;lib/ext/ocarina/adapters/selenium/test_campaign.py&lt;/code&gt; → pins &lt;code&gt;max_workers=get_max_workers()&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;ocarina--ocarina-with-ai-example&#34;&gt;&lt;code&gt;ocarina&lt;/code&gt; ↔ &lt;code&gt;ocarina-with-ai-example&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#ocarina--ocarina-with-ai-example&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;pip install . ruff mypy mypy-extensions typing-extensions pre-commit&lt;/code&gt; — the AI example pins its dep in &lt;code&gt;pyproject.toml&lt;/code&gt;: &lt;code&gt;ocarina&amp;gt;=1.0.3&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;act&lt;/code&gt; adapter is &lt;strong&gt;minimal&lt;/strong&gt; here. No &lt;code&gt;on_failure&lt;/code&gt; (CURA doesn&amp;rsquo;t throw deliberately random HTTP error pages). See &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/&#34;&gt;&lt;code&gt;../08-ai-example/&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;create_drivers_pool&lt;/code&gt; adapter is &lt;strong&gt;overridden&lt;/strong&gt; to build a &lt;em&gt;clean&lt;/em&gt; Chrome (password manager off, leak detection off). See &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/06-data-gaps/&#34;&gt;&lt;code&gt;../08-ai-example/06-data-gaps.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;ocarina-example--igoristan&#34;&gt;&lt;code&gt;ocarina-example&lt;/code&gt; ↔ &lt;code&gt;igoristan&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#ocarina-example--igoristan&#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;Direction&lt;/th&gt;&#xA;          &lt;th&gt;Contract&lt;/th&gt;&#xA;          &lt;th&gt;Detail&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;ocarina-example&lt;/code&gt; → &lt;code&gt;igoristan&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Public URLs&lt;/td&gt;&#xA;          &lt;td&gt;Everything goes through &lt;code&gt;https://mojo-molotov.github.io/igoristan/&amp;lt;route&amp;gt;&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;igoristan&lt;/code&gt; → &lt;code&gt;ocarina-example&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;none (the SUT doesn&amp;rsquo;t know it&amp;rsquo;s being tested)&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&lt;code&gt;src/constants/pages/&lt;/code&gt;:&lt;/p&gt;</description>
    </item>
    <item>
      <title>01.04 — Citations and claimed influences</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/01-philosophy/04-citations-and-influences/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/01-philosophy/04-citations-and-influences/</guid>
      <description>&lt;h1 id=&#34;0104citations-and-claimed-influences&#34;&gt;01.04 — Citations and claimed influences&lt;a class=&#34;anchor&#34; href=&#34;#0104citations-and-claimed-influences&#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 references cited in the Holy Book aren&amp;rsquo;t neutral — they set Ocarina&amp;rsquo;s &lt;strong&gt;theoretical frame&lt;/strong&gt;. We cut them by axis here, then offer the overall reading.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;technical-axisfunctional-programming-types-languages&#34;&gt;Technical axis — Functional programming, types, languages&lt;a class=&#34;anchor&#34; href=&#34;#technical-axisfunctional-programming-types-languages&#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;Influence&lt;/th&gt;&#xA;          &lt;th&gt;Origin&lt;/th&gt;&#xA;          &lt;th&gt;Relation to Ocarina&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;Railway Oriented Programming (ROP)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Scott Wlaschin (F#), a well-known pattern in functional programming&lt;/td&gt;&#xA;          &lt;td&gt;The heart of the framework. &lt;code&gt;Result[T] = Ok[T] | Fail&lt;/code&gt;, &lt;em&gt;short-circuit&lt;/em&gt;, fluent builder. See &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/03-railway/&#34;&gt;&lt;code&gt;../02-ocarina/03-railway/&lt;/code&gt;&lt;/a&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;λ-calculus (1930)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Alonzo Church&lt;/td&gt;&#xA;          &lt;td&gt;Cited as the invention that makes possible the “clean formalization” Ocarina pursues: “&lt;em&gt;What has been on our minds since the 1930s, &lt;strong&gt;since the invention of λ-calculus&lt;/strong&gt;, is finally scalable to the degree we always wanted it to be.&lt;/em&gt;”&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;McCulloch &amp;amp; Pitts (1943)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;First formalization of the artificial neuron&lt;/td&gt;&#xA;          &lt;td&gt;Cited as a reminder that the &lt;em&gt;real&lt;/em&gt; progress in AI is old and continuous, against the recent hype.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Python type system (PEP 695)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PEP 695 (Python 3.12+)&lt;/td&gt;&#xA;          &lt;td&gt;Prerequisite for the generic parametric typing that structures Ocarina (&lt;code&gt;TestSuite[Driver]&lt;/code&gt;, &lt;code&gt;Result[T]&lt;/code&gt;, etc.). See &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/06-pep-695-generics/&#34;&gt;&lt;code&gt;../03-functional/06-pep-695-generics.md&lt;/code&gt;&lt;/a&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;cultural-axisanti-narcissism-simplicity-kiss-done-right&#34;&gt;Cultural axis — Anti-narcissism, simplicity, KISS done right&lt;a class=&#34;anchor&#34; href=&#34;#cultural-axisanti-narcissism-simplicity-kiss-done-right&#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;Influence&lt;/th&gt;&#xA;          &lt;th&gt;Citation&lt;/th&gt;&#xA;          &lt;th&gt;Why&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;Terry Davis&lt;/strong&gt; (“the smartest programmer that&amp;rsquo;s ever lived”)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;“An idiot admires complexity, a genius admires simplicity, a physicist tries to make it simple…”&lt;/em&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Anti–ostentatious complexity argument; cornerstone of the “First feedbacks” chapter.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Lao-Tzu&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;“To attain knowledge, add things every day. To attain wisdom, remove things every day.”&lt;/em&gt;&lt;/td&gt;&#xA;          &lt;td&gt;A subtractive method: what remains after elimination.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Antoine de Saint-Exupéry&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;“Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.”&lt;/em&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Western restatement of the previous one. Closes the “First scenarios” chapter.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Alan Watts&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;“Muddy water is best cleared by leaving it alone.”&lt;/em&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Cited at the end of the “First jutsus” chapter: legitimizes a design that &lt;em&gt;refuses&lt;/em&gt; certain features (reactivity, async). Legitimizes stepping out of the “geek tricks” ecosystem.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Rainer Maria Rilke&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;“Live the questions now. Perhaps then, someday far in the future, you will gradually, without even noticing it, live your way into the answer.”&lt;/em&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Closes the “First steps” chapter. Stance toward practice: mastery comes from use.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Marcel Proust&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;“For the writer as well as for the painter, style is not a question of technique, but of vision.”&lt;/em&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Closes the “Extensibility” chapter. Justifies the refusal to impose a DSL: vision (POMs + actions) trumps technique (a format).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;political-axiscode-sovereignty-antistartup-nation&#34;&gt;Political axis — Code, sovereignty, anti–startup nation&lt;a class=&#34;anchor&#34; href=&#34;#political-axiscode-sovereignty-antistartup-nation&#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;Influence&lt;/th&gt;&#xA;          &lt;th&gt;Origin&lt;/th&gt;&#xA;          &lt;th&gt;Relation to Ocarina&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;&lt;em&gt;Code is Law&lt;/em&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Lawrence Lessig (1999), reclaimed by Ethereum (2015) as a positive ideal&lt;/td&gt;&#xA;          &lt;td&gt;Cited as the stance against “democratic governance of code”. The project claims it: “&lt;em&gt;Once again, with AI, that sorely missing digital body, let us scream it, as loud as we screamed&lt;/em&gt; &lt;strong&gt;‘HACK THE PLANET’&lt;/strong&gt; &lt;em&gt;back in &amp;lsquo;99:&lt;/em&gt; &lt;strong&gt;CODE IS LAW.&lt;/strong&gt;”&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;DHH — &lt;em&gt;I won&amp;rsquo;t let you pay me for my open source&lt;/em&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;David Heinemeier Hansson, personal blog&lt;/td&gt;&#xA;          &lt;td&gt;Cited to legitimize refusing unaligned contributions (“&lt;em&gt;Fuck You&lt;/em&gt;” references DHH&amp;rsquo;s famous slide).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Paul Graham — &lt;em&gt;Haters&lt;/em&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PG essay (&lt;code&gt;paulgraham.com/fh.html&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;Grounds the decision to “never waste time arguing when it isn&amp;rsquo;t worth it”.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Yegor Bugayenko — &lt;em&gt;Prompt&lt;/em&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;The quote “&lt;em&gt;Retry flaky blocks.&lt;/em&gt;”&lt;/td&gt;&#xA;          &lt;td&gt;Grounds the &lt;code&gt;transient_errors&lt;/code&gt; + linear retry policy (&lt;code&gt;TestFlow&lt;/code&gt;). Read also: &lt;em&gt;Angry Tests&lt;/em&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;The DAO hack (2016)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Hack of The DAO on Ethereum&lt;/td&gt;&#xA;          &lt;td&gt;Cited to point out that it was &lt;em&gt;bugs&lt;/em&gt; (“damned bugs, because of damned devs”) that set &lt;em&gt;Code is Law&lt;/em&gt; back, hence the importance of typing and rigor.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Lee Robinson (ex-Vercel)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;December 2025: Cursor replacing Sanity with Markdown files + AI&lt;/td&gt;&#xA;          &lt;td&gt;Backs the “return to raw data”.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Cluely (Roy Lee)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Cited as an illustration of entrepreneurial bullshit to avoid&lt;/td&gt;&#xA;          &lt;td&gt;Explicit contrast with the Ocarina philosophy.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;identity--underground-axis&#34;&gt;Identity / underground axis&lt;a class=&#34;anchor&#34; href=&#34;#identity--underground-axis&#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;Influence&lt;/th&gt;&#xA;          &lt;th&gt;Origin&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;LulzSec&lt;/strong&gt; — &lt;em&gt;“In the Lulzboat, salute, bitch, and show some respect.”&lt;/em&gt;&lt;/td&gt;&#xA;          &lt;td&gt;AntiSec (YTCracker, 2011)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;em&gt;“We Can Do All What You Can&amp;rsquo;t Do”&lt;/em&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;YogyaCarderLink&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;YTCracker — &lt;em&gt;Robots Will Definitely Take Your Job&lt;/em&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;SoundCloud link, background music of the “First feedbacks” chapter&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;em&gt;“HACK THE PLANET”&lt;/em&gt; (1999)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Emblematic slogan of the 1990s hacker scene + AntiSec (YTCracker, 2011)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Zone-H&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Historic web defacement archive&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;r/unixporn&lt;/strong&gt;, &lt;strong&gt;r/AntiTaff&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Subreddits&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;ai--modern-tooling-axis&#34;&gt;AI / modern tooling axis&lt;a class=&#34;anchor&#34; href=&#34;#ai--modern-tooling-axis&#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;Influence&lt;/th&gt;&#xA;          &lt;th&gt;Origin&lt;/th&gt;&#xA;          &lt;th&gt;Relation to Ocarina&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;Claude Code&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Anthropic&lt;/td&gt;&#xA;          &lt;td&gt;Reference tooling for the AI project (&lt;code&gt;ocarina-with-ai-example&lt;/code&gt;). Cited several times as the “bridge” replacing DSLs.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;IntelliCode (2018)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Microsoft&lt;/td&gt;&#xA;          &lt;td&gt;Cited as a temporal landmark (“we had already tasted it, well before IntelliCode”).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;R., the silo&amp;rsquo;s mad scientist&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Personal anecdote (a 2013 colleague, a private Emacs AI plugin)&lt;/td&gt;&#xA;          &lt;td&gt;Reminder that generative AI (“15,000 lines to delete to avoid writing 1,000”) existed before the public hype.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Prisma, Vercel, Cursor&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;SaaS ecosystem&lt;/td&gt;&#xA;          &lt;td&gt;Cited as practical cases of an AI-assisted &lt;em&gt;return to raw data&lt;/em&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;overall-coherence&#34;&gt;Overall coherence&lt;a class=&#34;anchor&#34; href=&#34;#overall-coherence&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;All these influences converge on &lt;strong&gt;three axes&lt;/strong&gt;:&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.03.04 — chain_actions and ChainRunner</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/03-railway/04-chain-actions-fold/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/03-railway/04-chain-actions-fold/</guid>
      <description>&lt;h1 id=&#34;020304chain_actions-and-chainrunner&#34;&gt;02.03.04 — &lt;code&gt;chain_actions&lt;/code&gt; and &lt;code&gt;ChainRunner&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#020304chain_actions-and-chainrunner&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing_with_railway/chain_actions.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing_with_railway/chain_actions.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;The primitive that makes the DSL &amp;ldquo;flat.&amp;rdquo; Without it, an N-step scenario is a staircase of &lt;code&gt;.then(...).failure(...).success(...).execute()&lt;/code&gt;. With it, it&amp;rsquo;s a list.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;parenthesis-hell&#34;&gt;“Parenthesis hell”&lt;a class=&#34;anchor&#34; href=&#34;#parenthesis-hell&#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;Solution:&lt;/p&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Flat, readable syntax&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;runner &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; chain_actions(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    action1&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;failure(h1)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;success(h2),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    action2&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;failure(h3)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;success(h4),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    action3&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;failure(h5)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;success(h6)&#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;chain &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;run()  &lt;span style=&#34;color:#6272a4&#34;&gt;# Execute when ready&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Benefits:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Lazy evaluation&lt;/strong&gt;: Build chain without executing&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Flat syntax&lt;/strong&gt;: No deep nesting&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Automatic short-circuiting&lt;/strong&gt;: Stops on first failure&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Composability&lt;/strong&gt;: ChainRunner is a value&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/blockquote&gt;&lt;p&gt;&lt;em&gt;(Excerpt from the docstring.)&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.04.04 — .then(...) and chain_validations(...)</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/04-invariants/04-then-chain-of-validations/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/04-invariants/04-then-chain-of-validations/</guid>
      <description>&lt;h1 id=&#34;020404then-and-chain_validations&#34;&gt;02.04.04 — &lt;code&gt;.then(...)&lt;/code&gt; and &lt;code&gt;chain_validations(...)&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#020404then-and-chain_validations&#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 distinct mechanisms to compose multiple validations into one.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;thennew_value&#34;&gt;&lt;code&gt;.then(new_value)&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#thennew_value&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;then&lt;/span&gt;[U](&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, new_value: U, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, name: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; ValidationStartBlock[U]:&#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;return&lt;/span&gt; ValidationStartBlock(new_value, &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_chain, name)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Switches the type&lt;/strong&gt; from &lt;code&gt;T&lt;/code&gt; to &lt;code&gt;U&lt;/code&gt;. The checker accepts the change.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Keeps the chain&lt;/strong&gt; (&lt;code&gt;self._chain&lt;/code&gt;). Past assertions stay in it; new ones &lt;em&gt;stack on top&lt;/em&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Swaps the &lt;code&gt;name&lt;/code&gt;&lt;/strong&gt;: the &lt;code&gt;name&lt;/code&gt; you pass to &lt;code&gt;.then()&lt;/code&gt; is the one used for the next assertions, not the previous block&amp;rsquo;s.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;canonical-use-case&#34;&gt;Canonical use case&lt;a class=&#34;anchor&#34; href=&#34;#canonical-use-case&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;validate(user, name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;user&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;.&lt;/span&gt;assert_that(is_not_none)&#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;.&lt;/span&gt;then(user&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;email, name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;user.email&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;.&lt;/span&gt;assert_that(is_email)&#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;.&lt;/span&gt;then(user&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;age, name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;user.age&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;.&lt;/span&gt;assert_that(is_positive)&#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;.&lt;/span&gt;assert_that(is_less_than_or_equal_to(&lt;span style=&#34;color:#bd93f9&#34;&gt;120&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;.&lt;/span&gt;execute()&#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;.&lt;/span&gt;raise_if_invalid()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Reads as: validate &lt;code&gt;user&lt;/code&gt;, then &lt;code&gt;user.email&lt;/code&gt;, then &lt;code&gt;user.age&lt;/code&gt;. &lt;em&gt;All&lt;/em&gt; pass → OK. &lt;em&gt;One&lt;/em&gt; fails → every error rolls up into the &lt;code&gt;AggregateInvariantViolationError&lt;/code&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.05.04 — TestSuite[Driver] — parallelized engine</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/05-orchestration/04-test-suite/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/05-orchestration/04-test-suite/</guid>
      <description>&lt;h1 id=&#34;020504testsuitedriverparallelized-engine&#34;&gt;02.05.04 — &lt;code&gt;TestSuite[Driver]&lt;/code&gt; — parallelized engine&lt;a class=&#34;anchor&#34; href=&#34;#020504testsuitedriverparallelized-engine&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/oc_test_suite.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing/oc_test_suite.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;One job&lt;/strong&gt;: run a sequence of &lt;code&gt;Test&lt;/code&gt;s in parallel, handle &lt;em&gt;saturation&lt;/em&gt;, ID filtering, and pre-execution invariant validation.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;constructor&#34;&gt;Constructor&lt;a class=&#34;anchor&#34; href=&#34;#constructor&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&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;font-style:italic&#34;&gt;self&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;*&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tests: Sequence[Test[Driver]],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    create_logger: Thunk[ILogger],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    drivers_pool: WebDriversPool[Driver],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    take_screenshot: ITakeScreenshot[Driver],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    act_counter: ActCounter &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    transient_errors: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;tuple&lt;/span&gt;[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt;[Exception], &lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    copy_indicator: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;COPY&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    put_space_after_copy_indicator: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;    max_retries_per_test: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    autoscreen_on_fail: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;    saturate_workers: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    only_ids: Iterable[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    exclude_ids: Iterable[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#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;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_guards_on_invoke(tests)&#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;# ... assignments ...&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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_tests &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; filter_tests_by_ids(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        tests,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        only&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;only_ids,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        exclude&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;exclude_ids,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        logger&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;create_logger()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;set_prefix(&lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;: filtering tests...&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;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Parameter&lt;/th&gt;&#xA;          &lt;th&gt;Type&lt;/th&gt;&#xA;          &lt;th&gt;Role&lt;/th&gt;&#xA;          &lt;th&gt;Default&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;name&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Suite name (appears in logs &amp;amp; reports)&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;tests&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Sequence[Test[Driver]]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;List of tests to run&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;create_logger&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Thunk[ILogger]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Logger factory&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;drivers_pool&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;WebDriversPool[Driver]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Shared pool&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;take_screenshot&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ITakeScreenshot[Driver]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Callable &lt;code&gt;(driver, logger, category) -&amp;gt; None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;act_counter&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ActCounter | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;If &lt;code&gt;None&lt;/code&gt; → &lt;code&gt;ThreadsBasedActCounter()&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;transient_errors&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;tuple[type[Exception], ...]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Exceptions that trigger a retry&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;()&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;copy_indicator&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Prefix for saturation-cloned tests&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;&amp;quot;COPY&amp;quot;&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;put_space_after_copy_indicator&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;[COPY 1]&lt;/code&gt; (&lt;code&gt;True&lt;/code&gt;) vs &lt;code&gt;[COPY1]&lt;/code&gt; (&lt;code&gt;False&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;True&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;max_retries_per_test&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;int | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Max retries for a potentially flaky test&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;None&lt;/code&gt; → &lt;code&gt;_DEFAULT_MAX_RETRIES_PER_TEST&lt;/code&gt; (8)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;autoscreen_on_fail&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Automatically capture one or several screenshots (burst) on failure&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;saturate_workers&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;bool | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Clone tests to saturate every worker if the pool is large enough&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;None&lt;/code&gt; → cascade: suite &amp;gt; campaign &amp;gt; bootstrap&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;only_ids&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Iterable[str]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;--only&lt;/code&gt; filter&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;()&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;exclude_ids&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Iterable[str]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;--exclude&lt;/code&gt; filter&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;()&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;guardrails&#34;&gt;Guardrails&lt;a class=&#34;anchor&#34; href=&#34;#guardrails&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;at-__init__-time&#34;&gt;At &lt;code&gt;__init__&lt;/code&gt; time&lt;a class=&#34;anchor&#34; href=&#34;#at-__init__-time&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_guards_on_invoke&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, tests: Sequence[Test[Driver]]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    validate_test_runners_ids(tests&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;tests, name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;tests&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;raise_if_invalid()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;→ Every &lt;code&gt;test_id&lt;/code&gt; must be unique. Otherwise: &lt;code&gt;AggregateInvariantViolationError&lt;/code&gt;. &lt;em&gt;Raised at construction&lt;/em&gt;, before any execution attempt. Duplicate a &lt;code&gt;test_id&lt;/code&gt; by accident and you find out instantly.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.10.04 — ActCounter &#43; ThreadsBasedActCounter</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/10-infra/04-act-counter/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/10-infra/04-act-counter/</guid>
      <description>&lt;h1 id=&#34;021004actcounter--threadsbasedactcounter&#34;&gt;02.10.04 — &lt;code&gt;ActCounter&lt;/code&gt; + &lt;code&gt;ThreadsBasedActCounter&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#021004actcounter--threadsbasedactcounter&#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;Thread-local counter of &lt;code&gt;act&lt;/code&gt; calls per attempt. Lets the report say &amp;ldquo;&lt;em&gt;this test made 17 steps before dying at step 18&lt;/em&gt;.&amp;rdquo;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;interface&#34;&gt;Interface&lt;a class=&#34;anchor&#34; href=&#34;#interface&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/ocarina/infra/act_counter.py&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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;ActCounter&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;reset&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;incr_act_call_count&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;default-implementation&#34;&gt;Default implementation&lt;a class=&#34;anchor&#34; href=&#34;#default-implementation&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/ocarina/opinionated/infra/act_counter.py&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;from&lt;/span&gt; threading &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; local&#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;_thread_local &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; local()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_COUNTER_KEY: Final[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;ocarina_counter&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;&#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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;ActCounter&lt;/span&gt;(_ActCounter):&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&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;return&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;getattr&lt;/span&gt;(_thread_local, _COUNTER_KEY, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;reset&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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:#8be9fd;font-style:italic&#34;&gt;setattr&lt;/span&gt;(_thread_local, _COUNTER_KEY, &lt;span style=&#34;color:#bd93f9&#34;&gt;0&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;incr_act_call_count&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;if&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;hasattr&lt;/span&gt;(_thread_local, _COUNTER_KEY):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;reset()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;setattr&lt;/span&gt;(_thread_local, _COUNTER_KEY, &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;getattr&lt;/span&gt;(_thread_local, _COUNTER_KEY) &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;threading.local()&lt;/code&gt;&lt;/strong&gt;: namespace where attributes are &lt;em&gt;per thread&lt;/em&gt;. Each thread has its own value.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;_COUNTER_KEY = &amp;quot;ocarina_counter&amp;quot;&lt;/code&gt;&lt;/strong&gt;: attribute key.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;No lock&lt;/strong&gt;: each thread only writes its own value, so no race.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;why-thread-local-rather-than-a-shared-counter--lock&#34;&gt;Why thread-local rather than a shared counter + lock?&lt;a class=&#34;anchor&#34; href=&#34;#why-thread-local-rather-than-a-shared-counter--lock&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Ocarina&amp;rsquo;s architecture says &lt;strong&gt;one thread = one test&lt;/strong&gt;. We&amp;rsquo;ve got no appetite for a whole &lt;em&gt;state monad&lt;/em&gt; — might as well park this &amp;ldquo;impurity&amp;rdquo; (still a piece of state) right here. That&amp;rsquo;s it.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.11.04 — Loggers: PrintLogger, FileLogger, PrintAndFileLogger, MutedLogger</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/04-loggers/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/04-loggers/</guid>
      <description>&lt;h1 id=&#34;021104loggers-printlogger-filelogger-printandfilelogger-mutedlogger&#34;&gt;02.11.04 — Loggers: &lt;code&gt;PrintLogger&lt;/code&gt;, &lt;code&gt;FileLogger&lt;/code&gt;, &lt;code&gt;PrintAndFileLogger&lt;/code&gt;, &lt;code&gt;MutedLogger&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#021104loggers-printlogger-filelogger-printandfilelogger-mutedlogger&#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;Source folder: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/tree/main/src/ocarina/opinionated/loggers&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/opinionated/loggers/&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Four canonical &lt;code&gt;ILogger&lt;/code&gt; implementations + a &lt;code&gt;create_matching_logger&lt;/code&gt; factory. All opt-in.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;create_matching_logger&#34;&gt;&lt;code&gt;create_matching_logger&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#create_matching_logger&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;LOGGERS_CHOICES &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;terminal&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;file&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;terminal+file&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;muted&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:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;create_matching_logger&lt;/span&gt;(mode: SupportedLogger) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; ILogger:&#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; mode &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;terminal&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;return&lt;/span&gt; PrintLogger()&#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; mode &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;file&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;return&lt;/span&gt; FileLogger(base_dir&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;get_default_log_dir())&#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; mode &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;terminal+file&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;return&lt;/span&gt; PrintAndFileLogger(base_dir&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;get_default_log_dir())&#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; mode &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;muted&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;return&lt;/span&gt; MutedLogger()&#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;raise&lt;/span&gt; ValueError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Unsupported logger mode: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;mode&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And:&lt;/p&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;get_default_log_dir&lt;/span&gt;() &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Path:&#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;return&lt;/span&gt; Path&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;cwd() &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;.ocarina_logs&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;→ By default, logs land in &lt;code&gt;.ocarina_logs/&lt;/code&gt; at the user project root. Gitignored by convention.&lt;/p&gt;&#xA;&lt;h2 id=&#34;printlogger&#34;&gt;&lt;code&gt;PrintLogger&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#printlogger&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;PrintLogger&lt;/span&gt;(ILogger):&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_prefix_thunk: Thunk[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_domain_taxonomy: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;tuple&lt;/span&gt;[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&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;&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;set_prefix&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, prefix_thunk: Thunk[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Self:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_prefix_thunk &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; prefix_thunk&#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;return&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;set_domain_taxonomy&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, taxonomy: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;tuple&lt;/span&gt;[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Self:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_domain_taxonomy &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; taxonomy&#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;return&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;raw&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;args, stream&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;kwargs) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;if&lt;/span&gt; stream &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            stream &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; sys&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;stdout&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;args, file&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;stream, &lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;kwargs)&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;info&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, msg, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;args, exc&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;kwargs):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_print_log(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;[INFO]&amp;#34;&lt;/span&gt;, msg, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;args, exc&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;exc, stream&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;sys&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;stdout)&#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;# ... id. for critical / error / warning / debug / success / test_name ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;State&lt;/strong&gt;: &lt;code&gt;_prefix_thunk&lt;/code&gt; (Thunk) + &lt;code&gt;_domain_taxonomy&lt;/code&gt; (tuple).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Output&lt;/strong&gt;: &lt;code&gt;sys.stdout&lt;/code&gt; (info, debug, success, test_name) or &lt;code&gt;sys.stderr&lt;/code&gt; (critical, error, warning, exception).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Format&lt;/strong&gt;: typically &lt;code&gt;&amp;lt;prefix&amp;gt; [LEVEL] &amp;lt;message&amp;gt;&lt;/code&gt;, with ANSI colors if terminal.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;cleanup()&lt;/code&gt;&lt;/strong&gt;: no-op.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;filelogger&#34;&gt;&lt;code&gt;FileLogger&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#filelogger&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@final&#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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;FileLogger&lt;/span&gt;(PrintLogger):&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&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;font-style:italic&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        base_dir: Path,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        with_flush_effect: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;        with_fallback_on_print_logger_when_no_taxonomy_effect: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_base_dir &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; base_dir&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_with_flush_effect &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; with_flush_effect&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_with_fallback_on_print_logger_when_no_taxonomy_effect &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            with_fallback_on_print_logger_when_no_taxonomy_effect&#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:#8be9fd;font-style:italic&#34;&gt;super&lt;/span&gt;()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&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;    @property&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_log_file_path&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Path:&#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; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;len&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_domain_taxonomy) &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            folder_path &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_base_dir&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            file_name &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_domain_taxonomy[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;.log&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;else&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            folder_path &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_base_dir&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;joinpath(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_domain_taxonomy[:&lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            file_name &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_domain_taxonomy[&lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;]&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;.log&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        folder_path&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;mkdir(parents&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;, exist_ok&lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;return&lt;/span&gt; folder_path &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; file_name&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;Taxonomy&lt;/th&gt;&#xA;          &lt;th&gt;Folder&lt;/th&gt;&#xA;          &lt;th&gt;File&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;(&amp;quot;&amp;quot;)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt; — &lt;/td&gt;&#xA;          &lt;td&gt;→ Fallback to PrintLogger if enabled&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;(&amp;quot;annoncements&amp;quot;,)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;base_dir&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;annoncements.log&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;(&amp;quot;e2e&amp;quot;, &amp;quot;Login&amp;quot;)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;base_dir/e2e&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Login.log&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;(&amp;quot;e2e&amp;quot;, &amp;quot;Login&amp;quot;, &amp;quot;happy paths&amp;quot;, &amp;quot;Login without OTP&amp;quot;)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;base_dir/e2e/Login/happy paths&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Login without OTP.log&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;The &lt;strong&gt;taxonomy is the folder tree&lt;/strong&gt;. The DOCX reports (see &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/05-plugins-reports/&#34;&gt;&lt;code&gt;05-plugins-reports.md&lt;/code&gt;&lt;/a&gt;) walk it recursively and emit one &lt;code&gt;.docx&lt;/code&gt; per test case.&lt;/p&gt;</description>
    </item>
    <item>
      <title>03.04 — reduce / fold in Ocarina</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/04-fold-reduce/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/04-fold-reduce/</guid>
      <description>&lt;h1 id=&#34;0304reduce--fold-in-ocarina&#34;&gt;03.04 — &lt;code&gt;reduce&lt;/code&gt; / fold in Ocarina&lt;a class=&#34;anchor&#34; href=&#34;#0304reduce--fold-in-ocarina&#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;One &lt;code&gt;reduce&lt;/code&gt; in the whole code base. But it&amp;rsquo;s &lt;strong&gt;the&lt;/strong&gt; central reducer — it&amp;rsquo;s what makes &lt;code&gt;chain_actions&lt;/code&gt; work.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/ocarina/dsl/testing_with_railway/chain_actions.py&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;from&lt;/span&gt; functools &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; reduce&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;chain_actions&lt;/span&gt;[T](&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    first: ActionSuccess[T], &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;rest: ActionSuccess[T]&#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;-&amp;gt;&lt;/span&gt; ChainRunner[T]:&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;thunk&lt;/span&gt;() &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; ActionChain[T]:&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;reducer&lt;/span&gt;(chain: ActionChain[T], step: ActionSuccess[T]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; ActionChain[T]:&#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; chain&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;has_failed():&#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;return&lt;/span&gt; chain&#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;return&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                chain&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;then(step&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;__action__)&#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;.&lt;/span&gt;failure(step&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;__failure_handler__)&#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;.&lt;/span&gt;success(step&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;__success_handler__)&#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;.&lt;/span&gt;execute()&#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;return&lt;/span&gt; reduce(reducer, rest, first&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute())&#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;return&lt;/span&gt; ChainRunner(thunk&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;thunk)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;anatomy&#34;&gt;Anatomy&lt;a class=&#34;anchor&#34; href=&#34;#anatomy&#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;FP concept&lt;/th&gt;&#xA;          &lt;th&gt;Python realization&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Accumulator type&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ActionChain[T]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Element type&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ActionSuccess[T]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Reducer&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;reducer(chain, step) -&amp;gt; ActionChain[T]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Initial value&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;first.execute()&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Iterable&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;rest&lt;/code&gt; (the &lt;code&gt;*rest&lt;/code&gt; tuple)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Short-circuit&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;if chain.has_failed(): return chain&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Result&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ActionChain[T]&lt;/code&gt; (the last one)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;execution-flow&#34;&gt;Execution flow&lt;a class=&#34;anchor&#34; href=&#34;#execution-flow&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;initial = first.execute()                                          # → ActionChain(ok=True, result=Ok(...))&#xA;&#xA;step 1 : reducer(&amp;lt;initial&amp;gt;, act2) :&#xA;  chain.has_failed() = False&#xA;  chain.then(act2.__action__).failure(...).success(...).execute()&#xA;  → action raises → Fail&#xA;  → failure_handler(exc)  ❌ FIRE&#xA;  → ActionChain(ok=False, result=Fail(exc))&#xA;&#xA;step 2 : reducer(&amp;lt;failed&amp;gt;, act3) :&#xA;  chain.has_failed() = True&#xA;  return chain                                                     ⚠️  short-circuit&#xA;&#xA;result of the reduce = &amp;lt;failed ActionChain&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;why-reduce&#34;&gt;Why &lt;code&gt;reduce&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#why-reduce&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Imperative approach (equivalent)&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;thunk&lt;/span&gt;() &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; ActionChain[T]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    chain &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; first&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&#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;for&lt;/span&gt; step &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; rest:&#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; chain&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;has_failed():&#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;break&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        chain &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            chain&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;then(step&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;__action__)&#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;.&lt;/span&gt;failure(step&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;__failure_handler__)&#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;.&lt;/span&gt;success(step&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;__success_handler__)&#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;.&lt;/span&gt;execute()&#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;return&lt;/span&gt; chain&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;Reduce&lt;/th&gt;&#xA;          &lt;th&gt;Imperative loop&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;A single &lt;strong&gt;value&lt;/strong&gt; flows (the &lt;code&gt;chain&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;A state + a mutation&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Reducer = pure function (testable in isolation)&lt;/td&gt;&#xA;          &lt;td&gt;Inline logic&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Explicit &amp;ldquo;&lt;em&gt;accumulator&lt;/em&gt;&amp;rdquo; semantics&lt;/td&gt;&#xA;          &lt;td&gt;Implicit logic&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Functional standard&lt;/td&gt;&#xA;          &lt;td&gt;Imperative standard&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;An &lt;strong&gt;expression of intent&lt;/strong&gt;: we &lt;em&gt;fold&lt;/em&gt; a list onto an accumulator. Semantics jump out on the first read. This is what&amp;rsquo;s called a &lt;strong&gt;catamorphism&lt;/strong&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>04.04 — Static typing tests (pytest-mypy-plugins)</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/04-internal-tests/04-mypy-plugins-types/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/04-internal-tests/04-mypy-plugins-types/</guid>
      <description>&lt;h1 id=&#34;0404static-typing-tests-pytest-mypy-plugins&#34;&gt;04.04 — &lt;em&gt;Static typing&lt;/em&gt; tests (&lt;code&gt;pytest-mypy-plugins&lt;/code&gt;)&lt;a class=&#34;anchor&#34; href=&#34;#0404static-typing-tests-pytest-mypy-plugins&#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;Five &lt;code&gt;*test_types.yml&lt;/code&gt; files that test &lt;em&gt;the checker&amp;rsquo;s behavior&lt;/em&gt;. A testing dimension &lt;strong&gt;unique&lt;/strong&gt; to Ocarina in its way: we make sure that what &lt;em&gt;should&lt;/em&gt; be a &lt;em&gt;mypy&lt;/em&gt; error actually is one.&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;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;tests/&#xA;├── dsl/&#xA;│   ├── invariants/test_types.yml&#xA;│   └── testing_with_railway/test_types.yml&#xA;└── opinionated/&#xA;    ├── cli/test_types.yml&#xA;    ├── infra/test_types.yml&#xA;    └── plugins/reports/...   (snapshots — not YAML)&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;yaml&#34;&gt;YAML&lt;a class=&#34;anchor&#34; href=&#34;#yaml&#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;case&lt;/span&gt;: compatible_predicate_types&#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;description&lt;/span&gt;: Predicates should accept values of compatible types&#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;main&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;    from ocarina.dsl.invariants.validate import validate&#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;    from ocarina.dsl.invariants.assertions import is_email, is_positive&#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;&#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;    validate(1234).assert_that(is_positive)&#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;    validate(&amp;#34;a@a.com&amp;#34;).assert_that(is_email)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Without &lt;code&gt;out:&lt;/code&gt; → we expect &lt;code&gt;mypy&lt;/code&gt; to pass &lt;strong&gt;without error&lt;/strong&gt; on the given code.&lt;/p&gt;</description>
    </item>
    <item>
      <title>05.04 — Key UI components</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/05-igoristan/04-key-components/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/05-igoristan/04-key-components/</guid>
      <description>&lt;h1 id=&#34;0504key-ui-components&#34;&gt;05.04 — Key UI components&lt;a class=&#34;anchor&#34; href=&#34;#0504key-ui-components&#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 &lt;code&gt;src/components/&lt;/code&gt; files and their role in the playground.&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;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;src/components/&#xA;├── BackToHome.tsx              # back-to-HOME button&#xA;├── BackToSicily.tsx            # variant&#xA;├── Button.tsx                  # Tailwind-styled button&#xA;├── ChaoticForm.tsx             # ⭐ form with .catch-me-if-you-can elements&#xA;├── Dropzone.tsx                # ⭐ react-dropzone wrapper for SACRED_UPLOAD&#xA;├── ErrorCode.tsx               # component showing an HTTP code (used by RANDOM_ERROR)&#xA;├── FakeUpload/                 # fake upload component (animation, no real POST)&#xA;├── InternalErrorMsg.tsx        # generic error message&#xA;├── Label.tsx                   # form label&#xA;├── Link.tsx                    # styled &amp;lt;a&amp;gt; wrapper&#xA;├── Loading.tsx                 # generic spinner&#xA;├── LoginForm.tsx               # ⭐ login form + MFA OTP&#xA;├── PlusButton.tsx              # + button (corsicamon: add to collection)&#xA;├── RandomBibleVerse.tsx        # random bible verse&#xA;├── RandomLoader.tsx            # ⭐ random loader&#xA;├── UploadPreview.tsx           # preview of an uploaded file&#xA;└── XButton.tsx                 # X close button&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;⭐ = key component for Ocarina scenarios.&lt;/p&gt;</description>
    </item>
    <item>
      <title>06.04 — GET /api/corsicadex?id=N</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/06-tests-workers/04-corsicadex/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/06-tests-workers/04-corsicadex/</guid>
      <description>&lt;h1 id=&#34;0604get-apicorsicadexidn&#34;&gt;06.04 — &lt;code&gt;GET /api/corsicadex?id=N&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#0604get-apicorsicadexidn&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/tests-workers/blob/main/api/corsicadex.ts&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;api/corsicadex.ts&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;type&lt;/span&gt; { NextRequest } &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;next/server&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;import&lt;/span&gt; isAuthorized &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;../lib/isAuthorized&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;import&lt;/span&gt; { corsicaDexData } &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;../consts/corsicadexData&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:#ff79c6&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt; config &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; { runtime&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;edge&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:#ff79c6&#34;&gt;const&lt;/span&gt; corsHeaders &lt;span style=&#34;color:#ff79c6&#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:#f1fa8c&#34;&gt;&amp;#34;Access-Control-Allow-Origin&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&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;Access-Control-Allow-Methods&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;GET, OPTIONS&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;Access-Control-Allow-Headers&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;x-api-key, Content-Type&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;as&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;const&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;export&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;function&lt;/span&gt; handler(request: &lt;span style=&#34;color:#8be9fd&#34;&gt;NextRequest&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;if&lt;/span&gt; (request.method &lt;span style=&#34;color:#ff79c6&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;OPTIONS&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;return&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Response(&lt;span style=&#34;color:#ff79c6&#34;&gt;null&lt;/span&gt;, { status: &lt;span style=&#34;color:#8be9fd&#34;&gt;200&lt;/span&gt;, headers: &lt;span style=&#34;color:#8be9fd&#34;&gt;corsHeaders&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;&#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; (&lt;span style=&#34;color:#ff79c6&#34;&gt;!&lt;/span&gt;isAuthorized(request)) {&#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;return&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Response(JSON.stringify({ error&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Unauthorized&amp;#34;&lt;/span&gt; }), {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      status: &lt;span style=&#34;color:#8be9fd&#34;&gt;401&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      headers&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; { &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;, ...corsHeaders },&#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;  }&#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;const&lt;/span&gt; url &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; URL(request.url);&#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;const&lt;/span&gt; id &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; url.searchParams.&lt;span style=&#34;color:#ff79c6&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;id&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:#ff79c6&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#ff79c6&#34;&gt;!&lt;/span&gt;id) {&#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;return&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Response(JSON.stringify({ error&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Missing id parameter&amp;#34;&lt;/span&gt; }), {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      status: &lt;span style=&#34;color:#8be9fd&#34;&gt;400&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      headers&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; { &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;, ...corsHeaders },&#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;  }&#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;const&lt;/span&gt; corsicamonId &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;parseInt&lt;/span&gt;(id, &lt;span style=&#34;color:#bd93f9&#34;&gt;10&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;if&lt;/span&gt; (&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;isNaN&lt;/span&gt;(corsicamonId)) {&#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;return&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Response(JSON.stringify({ error&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Invalid ID&amp;#34;&lt;/span&gt; }), {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      status: &lt;span style=&#34;color:#8be9fd&#34;&gt;400&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      headers&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; { &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;, ...corsHeaders },&#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;  }&#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;const&lt;/span&gt; corsicamon &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; corsicaDexData.find((p) &lt;span style=&#34;color:#ff79c6&#34;&gt;=&amp;gt;&lt;/span&gt; p.id &lt;span style=&#34;color:#ff79c6&#34;&gt;===&lt;/span&gt; corsicamonId);&#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;if&lt;/span&gt; (&lt;span style=&#34;color:#ff79c6&#34;&gt;!&lt;/span&gt;corsicamon) {&#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;return&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Response(JSON.stringify({ error&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Corsicamon not found&amp;#34;&lt;/span&gt; }), {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      status: &lt;span style=&#34;color:#8be9fd&#34;&gt;404&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      headers&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; { &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;, ...corsHeaders },&#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;  }&#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;return&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Response(JSON.stringify(corsicamon), {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    headers&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; { &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;, ...corsHeaders },&#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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;mechanics&#34;&gt;Mechanics&lt;a class=&#34;anchor&#34; href=&#34;#mechanics&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Static lookup in a TypeScript array.&lt;/p&gt;</description>
    </item>
    <item>
      <title>07.04 — Corsicamon scenarios</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/04-scenarios-corsicamon/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/04-scenarios-corsicamon/</guid>
      <description>&lt;h1 id=&#34;0704corsicamon-scenarios&#34;&gt;07.04 — Corsicamon scenarios&lt;a class=&#34;anchor&#34; href=&#34;#0704corsicamon-scenarios&#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;Igoristan&amp;rsquo;s &amp;ldquo;&lt;em&gt;Corsican Pokédex (Corsicadex)&lt;/em&gt;&amp;rdquo; page. API key input, random draw with 1/5 fail, &lt;code&gt;transient_errors&lt;/code&gt; for &lt;code&gt;Error(&#39;lol&#39;)&lt;/code&gt;, and POM-internal retries against &amp;ldquo;&lt;em&gt;Corsicamons loaded / network error&lt;/em&gt;&amp;rdquo; states.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;campaign&#34;&gt;Campaign&lt;a class=&#34;anchor&#34; href=&#34;#campaign&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/tests/campaigns/corsicamon.py&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;create_igoristan_corsicamon_campaign&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, drivers_pool) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; TestCampaign:&#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;return&lt;/span&gt; TestCampaign(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Corsicamon&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        suites&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;[&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            create_corsicamon_happy_paths_test_suite(drivers_pool&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;drivers_pool),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            create_corsicamon_unhappy_paths_test_suite(drivers_pool&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;drivers_pool),&#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;    )&#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;&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;create_igoristan_corsicamon_smoke_campaign&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, drivers_pool) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; TestCampaign:&#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;return&lt;/span&gt; TestCampaign(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Corsicamon - Smoke&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        suites&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;[create_corsicamon_smoke_tests_suite(drivers_pool&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;drivers_pool)],&#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;h2 id=&#34;smoke-test&#34;&gt;Smoke test&lt;a class=&#34;anchor&#34; href=&#34;#smoke-test&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# tests/scenarios/corsicamon/enter_api_key.py&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;scenario_enter_api_key_smoke&lt;/span&gt;(driver, logger):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; CorsicamonEnterApiKeyPage(driver&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;driver)&#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;...&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;return&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        drive_page(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(page, open_corsicamon_page)&lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(page, verify_enter_api_key_screen)&lt;span style=&#34;color:#ff79c6&#34;&gt;...&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;    ]&#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;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test_enter_api_key_smoke &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; create_selenium_test(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Corsicamon - Smoke - enter API key screen visible&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    test_scenario&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt; driver, logger: Scenario(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        test_chain&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;scenario_enter_api_key_smoke(driver, logger)&#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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Verifies the API key entry page appears. Fails → smoke fails → main campaigns skipped.&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>09.03.04 — Comprehend skills</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/04-comprehend/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/04-comprehend/</guid>
      <description>&lt;h1 id=&#34;090304comprehend-skills&#34;&gt;09.03.04 — Comprehend skills&lt;a class=&#34;anchor&#34; href=&#34;#090304comprehend-skills&#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;Skills that help the AI &lt;strong&gt;understand&lt;/strong&gt; a project or ecosystem before acting.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;listing-potentially-non-exhaustive&#34;&gt;Listing (potentially non-exhaustive)&lt;a class=&#34;anchor&#34; href=&#34;#listing-potentially-non-exhaustive&#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;Skill&lt;/th&gt;&#xA;          &lt;th&gt;Target&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;assess-test-base&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Catalogs the existing test base&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;assess-ecosystem&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Bounded public research, capped by a token budget&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;assess-impact&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Forward impact analysis: a change&amp;rsquo;s blast radius through the dependency graph&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;understand-sut-constraints&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Understands the SUT&amp;rsquo;s &amp;ldquo;boundaries&amp;rdquo; so as not to exceed them&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;understand-ocarina&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Routes by question class: Holy Book for reference, the from-ocarina-to-igor book for intent; then Ocarina source + the adapter-matched worked example&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;understand-ocarina&#34;&gt;&lt;code&gt;understand-ocarina&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#understand-ocarina&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;input  : user question — routed by class:&#xA;            - &amp;#34;how do I do a match_page?&amp;#34;        (reference: what a primitive is) → Holy Book&#xA;            - &amp;#34;why is the suite built on Railway?&amp;#34; (intent / cartography / why)    → the from-ocarina-to-igor book&#xA;output : Claude loads, by question class:&#xA;            - reference  → relevant Holy Book pages (https://mojo-molotov.github.io/ocarina-holy-book)&#xA;            - intent     → relevant chapters of the from-ocarina-to-igor book (https://mojo-molotov.github.io/from-ocarina-to-igor/)&#xA;            - behaviour  → Ocarina source clone, if the docs don&amp;#39;t cover it&#xA;            - shape      → the worked example matching the project&amp;#39;s driver adapter:&#xA;                              Selenium   → ocarina-example, ocarina-with-ai-example&#xA;                              Playwright → ocarina-with-playwright-example&#xA;         then answers with citations (tier + page/chapter URL or file:line)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;SKILL.md&lt;/code&gt;:&lt;/p&gt;</description>
    </item>
    <item>
      <title>09.04 — CLAUDE.md and CLAUDE.slim.md</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/04-claude-md/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/04-claude-md/</guid>
      <description>&lt;h1 id=&#34;0904claudemd-and-claudeslimmd&#34;&gt;09.04 — &lt;code&gt;CLAUDE.md&lt;/code&gt; and &lt;code&gt;CLAUDE.slim.md&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#0904claudemd-and-claudeslimmd&#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 Holy Book &lt;strong&gt;actively publishes&lt;/strong&gt; the &lt;code&gt;CLAUDE.md&lt;/code&gt; files. This lets an LLM fetch the project&amp;rsquo;s docs without going through the repo.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;the-two-files&#34;&gt;The two files&lt;a class=&#34;anchor&#34; href=&#34;#the-two-files&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ai/&#xA;├── CLAUDE.md       # full version (rules + project layout)&#xA;└── CLAUDE.slim.md  # short version (rules only)&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;public-urls&#34;&gt;Public URLs&lt;a class=&#34;anchor&#34; href=&#34;#public-urls&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;https://mojo-molotov.github.io/ocarina-holy-book/CLAUDE.md&#xA;https://mojo-molotov.github.io/ocarina-holy-book/CLAUDE.slim.md&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Holy Book quote (chapter &amp;ldquo;&lt;em&gt;Using Ocarina with AI&lt;/em&gt;&amp;rdquo;):&lt;/p&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Slim when context is heavy; full for onboarding and reviews. Full wins on disagreement.&lt;/p&gt;</description>
    </item>
    <item>
      <title>10.04 — ocarina-with-ai-example workflows</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/04-ai-workflows/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/04-ai-workflows/</guid>
      <description>&lt;h1 id=&#34;1004ocarina-with-ai-example-workflows&#34;&gt;10.04 — &lt;code&gt;ocarina-with-ai-example&lt;/code&gt; workflows&lt;a class=&#34;anchor&#34; href=&#34;#1004ocarina-with-ai-example-workflows&#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;See also &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/09-ci-matrix/&#34;&gt;&lt;code&gt;../08-ai-example/09-ci-matrix.md&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;a class=&#34;anchor&#34; href=&#34;#overview&#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;Workflow&lt;/th&gt;&#xA;          &lt;th&gt;Trigger&lt;/th&gt;&#xA;          &lt;th&gt;OS&lt;/th&gt;&#xA;          &lt;th&gt;Effect&lt;/th&gt;&#xA;          &lt;th&gt;Note&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;ai_proof_ci.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;push main, PR, dispatch&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu × py 3.14&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ruff format src/ --check&lt;/code&gt; + &lt;code&gt;ruff check src/&lt;/code&gt; + &lt;code&gt;mypy src/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PR gate, fast&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;ai_proof_e2e.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;dispatch&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu × &lt;strong&gt;firefox+chrome matrix&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Heroku warm-up + run + sed filter ChromeDriver C++ stacks + upload&lt;/td&gt;&#xA;          &lt;td&gt;Manual only&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;differences-vs-ocarina-example&#34;&gt;Differences vs &lt;code&gt;ocarina-example&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#differences-vs-ocarina-example&#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;Aspect&lt;/th&gt;&#xA;          &lt;th&gt;&lt;code&gt;ocarina-example&lt;/code&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;code&gt;ocarina-with-ai-example&lt;/code&gt;&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;Browser matrix&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Firefox only (e2e.yml)&lt;/td&gt;&#xA;          &lt;td&gt;Firefox &lt;strong&gt;+&lt;/strong&gt; Chrome (parallelized)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;SUT&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Igoristan (GitHub Pages)&lt;/td&gt;&#xA;          &lt;td&gt;CURA (Heroku eco-dyno)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Pre-run&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;warm-up Redis client&lt;/td&gt;&#xA;          &lt;td&gt;warm-up Heroku dyno&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Output filter&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;none&lt;/em&gt;&lt;/td&gt;&#xA;          &lt;td&gt;sed filters ChromeDriver C++ stack frames&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&lt;code&gt;WAIT_TIMEOUT&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;default 10&lt;/td&gt;&#xA;          &lt;td&gt;15 (compensates for Heroku latency)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;heroku-warm-up&#34;&gt;Heroku warm-up&lt;a class=&#34;anchor&#34; href=&#34;#heroku-warm-up&#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-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -sf --retry &lt;span style=&#34;color:#bd93f9&#34;&gt;6&lt;/span&gt; --retry-delay &lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt; --retry-all-errors &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;  --max-time &lt;span style=&#34;color:#bd93f9&#34;&gt;30&lt;/span&gt; https://katalon-demo-cura.herokuapp.com/ &amp;gt; /dev/null&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Dyno is warm&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;CURA runs on a Heroku dyno that sleeps after inactivity.&#xA;Tests hitting a cold dyno produce timeouts (false fails) and slow&#xA;confirmations (false passes on gap tests). Wake it explicitly&#xA;before any browser opens.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.04 — Zone-H, IRC, EFnet</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/04-zoneh-irc-efnet/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/04-zoneh-irc-efnet/</guid>
      <description>&lt;h1 id=&#34;1204zone-h-irc-efnet&#34;&gt;12.04 — Zone-H, IRC, EFnet&lt;a class=&#34;anchor&#34; href=&#34;#1204zone-h-irc-efnet&#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 infrastructures without which the 1998-2012 hacker scene would never have been the same: &lt;strong&gt;Zone-H&lt;/strong&gt; (deface archive), &lt;strong&gt;IRC&lt;/strong&gt;, &lt;strong&gt;EFnet&lt;/strong&gt; (the historical IRC network). The Holy Book cites Zone-H only once, but it&amp;rsquo;s a keyword that opens an entire infrastructure.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;1-zone-hdeface-archive-2002--today&#34;&gt;1. Zone-H — deface archive (2002 → today)&lt;a class=&#34;anchor&#34; href=&#34;#1-zone-hdeface-archive-2002--today&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;foundation&#34;&gt;Foundation&lt;a class=&#34;anchor&#34; href=&#34;#foundation&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Date&lt;/strong&gt;: March 2, 2002.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;HQ&lt;/strong&gt;: Estonia.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Founder&lt;/strong&gt;: &lt;strong&gt;Roberto Preatoni&lt;/strong&gt; (alias &lt;strong&gt;&lt;code&gt;Sys64738&lt;/code&gt;&lt;/strong&gt;).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;URL&lt;/strong&gt;: &lt;a href=&#34;https://zone-h.org&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;zone-h.org&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;function&#34;&gt;Function&lt;a class=&#34;anchor&#34; href=&#34;#function&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;An attacker defaces a site (replaces the homepage with their own version, signed).&lt;/li&gt;&#xA;&lt;li&gt;The attacker (or a witness) submits the URL to Zone-H.&lt;/li&gt;&#xA;&lt;li&gt;Zone-H moderates: verifies the deface is authentic.&lt;/li&gt;&#xA;&lt;li&gt;If OK: the defaced site is &lt;strong&gt;archived forever&lt;/strong&gt; on Zone-H (HTML+CSS+assets snapshot).&lt;/li&gt;&#xA;&lt;li&gt;The deface author signs with their alias. Sourcing is public.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;impact&#34;&gt;Impact&lt;a class=&#34;anchor&#34; href=&#34;#impact&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;For 10-15 years, Zone-H was &lt;strong&gt;the public scoreboard&lt;/strong&gt; of the global &lt;em&gt;grey hat&lt;/em&gt; scene. Every notable deface was archived. For a &lt;em&gt;script kiddie&lt;/em&gt;, climbing the Zone-H rankings was &lt;em&gt;the&lt;/em&gt; goal to &lt;em&gt;prove&lt;/em&gt;:&lt;/p&gt;</description>
    </item>
    <item>
      <title>01.05 — The project&#39;s political stance</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/01-philosophy/05-political-stance/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/01-philosophy/05-political-stance/</guid>
      <description>&lt;h1 id=&#34;0105the-projects-political-stance&#34;&gt;01.05 — The project&amp;rsquo;s political stance&lt;a class=&#34;anchor&#34; href=&#34;#0105the-projects-political-stance&#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 Holy Book doesn&amp;rsquo;t separate philosophy from the &lt;strong&gt;politics of the repo&lt;/strong&gt; itself. Five operating commitments come out of it.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;1-incorruptibility&#34;&gt;1. Incorruptibility&lt;a class=&#34;anchor&#34; href=&#34;#1-incorruptibility&#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;Sharing Ocarina means &lt;strong&gt;sharing a car I have maintained entirely myself, for myself, therefore with great care&lt;/strong&gt;. Still, it is shared as-is, and will remain so.&#xA;It&amp;rsquo;s &lt;em&gt;my&lt;/em&gt; car.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;I channeled all my anger into it, to pour all my love into it.&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.03.05 — create_act and its hooks</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/03-railway/05-create-act-hooks/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/03-railway/05-create-act-hooks/</guid>
      <description>&lt;h1 id=&#34;020305create_act-and-its-hooks&#34;&gt;02.03.05 — &lt;code&gt;create_act&lt;/code&gt; and its hooks&lt;a class=&#34;anchor&#34; href=&#34;#020305create_act-and-its-hooks&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing_with_railway/constructors/create_act.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing_with_railway/constructors/create_act.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;The &lt;strong&gt;low-level primitive&lt;/strong&gt; that builds a &lt;em&gt;test step&lt;/em&gt; constructor. You call it &lt;strong&gt;once&lt;/strong&gt; to mint a project-specific &lt;code&gt;act&lt;/code&gt; verb. In theory a complex project might want several distinct &lt;code&gt;act&lt;/code&gt; verbs with different hooks, but no real-world need has shown up yet — recommendation: create &lt;strong&gt;one&lt;/strong&gt;. Not a singleton, just a convention.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;signature&#34;&gt;Signature&lt;a class=&#34;anchor&#34; href=&#34;#signature&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;create_act&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pom: TPOM,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    action: Callable[[TPOM], TPOM],&#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;*&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    on_failure: Callable[[TPOM, Exception], Fail] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    on_run_effect: Effect &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    act_counter_effect: Effect &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;-&amp;gt;&lt;/span&gt; ActionStart[TPOM]:&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;Parameter&lt;/th&gt;&#xA;          &lt;th&gt;Position&lt;/th&gt;&#xA;          &lt;th&gt;Type&lt;/th&gt;&#xA;          &lt;th&gt;Role&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;pom&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;positional&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TPOM&lt;/code&gt; (bound &lt;code&gt;POMBase&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;The page (or any &lt;code&gt;POMBase&lt;/code&gt; subclass) you&amp;rsquo;re acting on.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;action&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;positional&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Callable[[TPOM], TPOM]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;The function that acts on the page and returns the page (fluent). Identity-typed.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;on_failure&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;keyword-only&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Callable[[TPOM, Exception], Fail] | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Hook for &lt;strong&gt;refining&lt;/strong&gt; the &lt;code&gt;Fail&lt;/code&gt;: if you reach this, you&amp;rsquo;ve already failed; the hook just lets you type the error more precisely (e.g. &lt;code&gt;HttpErrorPageReachedError&lt;/code&gt;).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;on_run_effect&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;keyword-only&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Effect | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Side effect called &lt;strong&gt;before&lt;/strong&gt; the action (e.g.: log a step number).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;act_counter_effect&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;keyword-only&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Effect | None&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Additional&lt;/em&gt; side effect called before the action. Default: increments the act counter.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;run_action&lt;/span&gt;() &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Result[TPOM]:&#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;try&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;if&lt;/span&gt; act_counter_effect:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act_counter_effect()&#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;else&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ThreadsBasedActCounter()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;incr_act_call_count()&#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;if&lt;/span&gt; on_run_effect:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            on_run_effect()&#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;        result_pom &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; action(pom)&#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;return&lt;/span&gt; Ok(result_pom)&#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;except&lt;/span&gt; Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;as&lt;/span&gt; exc:  &lt;span style=&#34;color:#6272a4&#34;&gt;# noqa: BLE001&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;if&lt;/span&gt; on_failure:&#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;return&lt;/span&gt; on_failure(pom, exc)&#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;return&lt;/span&gt; Fail(error&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;exc)&#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;return&lt;/span&gt; ActionStart(run_action)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;The &lt;code&gt;try&lt;/code&gt; wraps EVERYTHING&lt;/strong&gt;: &lt;code&gt;act_counter_effect&lt;/code&gt;, &lt;code&gt;on_run_effect&lt;/code&gt;, &lt;em&gt;and&lt;/em&gt; the action. If any side effect raises (say &lt;code&gt;on_run_effect&lt;/code&gt; hits a dead resource), it counts as a test-step failure. Everything that runs &amp;ldquo;during the test step&amp;rdquo; IS the test step.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Order is deterministic&lt;/strong&gt;: counter → run effect → action. The counter ticks &lt;em&gt;even if&lt;/em&gt; the action ends up failing. Intentional — we want to know how many steps were &lt;em&gt;attempted&lt;/em&gt;, not how many passed.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;on_failure&lt;/code&gt; can return an enriched &lt;code&gt;Fail&lt;/code&gt;&lt;/strong&gt;: that&amp;rsquo;s where a &lt;code&gt;WebDriverException&lt;/code&gt; becomes an &lt;code&gt;HttpErrorPageReachedError&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;default-actcounter-threadsbasedactcounter&#34;&gt;Default &lt;code&gt;ActCounter&lt;/code&gt;: &lt;code&gt;ThreadsBasedActCounter&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#default-actcounter-threadsbasedactcounter&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; ocarina.opinionated.infra.act_counter &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; ActCounter &lt;span style=&#34;color:#ff79c6&#34;&gt;as&lt;/span&gt; ThreadsBasedActCounter&#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;# ...&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;if&lt;/span&gt; act_counter_effect:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    act_counter_effect()&#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;else&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ThreadsBasedActCounter()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;incr_act_call_count()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Source: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/infra/act_counter.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/opinionated/infra/act_counter.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.04.05 — BusinessInvariantValidator vs FrameworkInvariantValidator</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/04-invariants/05-business-vs-framework-validator/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/04-invariants/05-business-vs-framework-validator/</guid>
      <description>&lt;h1 id=&#34;020405businessinvariantvalidator-vs-frameworkinvariantvalidator&#34;&gt;02.04.05 — &lt;code&gt;BusinessInvariantValidator&lt;/code&gt; vs &lt;code&gt;FrameworkInvariantValidator&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#020405businessinvariantvalidator-vs-frameworkinvariantvalidator&#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 factories, identical code, exist purely &lt;strong&gt;to signal intent&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_BaseCustomInvariantValidator&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    @staticmethod&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;create&lt;/span&gt;[T](&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        value: T,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        name: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        build_chain: Callable[[ValidationStartBlock[T], T], ValidationAssertBlock[T]],&#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;-&amp;gt;&lt;/span&gt; ValidationAssertBlock[T]:&#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;return&lt;/span&gt; _create_business_invariant_validator(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            value, name, &lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt; chain: build_chain(chain, value)&#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;&#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;@final&#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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;BusinessInvariantValidator&lt;/span&gt;(_BaseCustomInvariantValidator):&#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;&amp;#34;&amp;#34;Factory for creating business domain invariant validators.&#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;&#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;    Use this for domain-specific validation logic (e.g., &amp;#34;user must be adult&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;    &amp;#34;order total must match items&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;    &amp;#34;&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;&#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;@final&#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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;FrameworkInvariantValidator&lt;/span&gt;(_BaseCustomInvariantValidator):&#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;&amp;#34;&amp;#34;Factory for creating framework-level invariant validators.&#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;&#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;    Use this for technical/framework validation logic (e.g., &amp;#34;config must be valid&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;    &amp;#34;test structure must be correct&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;    &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_create_business_invariant_validator&lt;/span&gt;[T](&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    value: T,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    build_chain: ValidationChainBuilder[T],&#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;-&amp;gt;&lt;/span&gt; ValidationAssertBlock[T]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    start_block &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; validate(value, name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;name)&#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;return&lt;/span&gt; build_chain(start_block)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;why-two-classes&#34;&gt;Why two classes?&lt;a class=&#34;anchor&#34; href=&#34;#why-two-classes&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;code&gt;BusinessInvariantValidator&lt;/code&gt; flags a business rule; &lt;code&gt;FrameworkInvariantValidator&lt;/code&gt;, a technical framework rule. Intent jumps off the page.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Grep&lt;/strong&gt;. &lt;code&gt;FrameworkInvariantValidator.create&lt;/code&gt; finds &lt;em&gt;every framework invariant&lt;/em&gt; in one search. Same for business rules on the project side.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Maintainability&lt;/strong&gt;. Want to add behavior (different log, different stack trace) to one or the other? Do it without touching every call-site.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;usage&#34;&gt;Usage&lt;a class=&#34;anchor&#34; href=&#34;#usage&#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;Invariant&lt;/th&gt;&#xA;          &lt;th&gt;Category&lt;/th&gt;&#xA;          &lt;th&gt;Where it lives&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;validate_workers_amount&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Framework&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/ocarina/custom_invariants/testing/workers.py&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;validate_test_runners_ids&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Framework&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/ocarina/custom_invariants/testing/oc_test_runners_ids.py&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;validate_test_runners_names&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Framework&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/ocarina/custom_invariants/testing/oc_test_runners_names.py&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;validate_test_suites_names&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Framework&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/ocarina/custom_invariants/testing/oc_test_suites_names.py&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;validate_test_campaigns_names&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Framework&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/ocarina/custom_invariants/testing/oc_test_campaigns_names.py&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;validate_test_cycle_name&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Framework&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/ocarina/custom_invariants/testing/oc_test_cycles_names.py&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;em&gt;Business example&lt;/em&gt;: “the username must be on the whitelist”&lt;/td&gt;&#xA;          &lt;td&gt;Business&lt;/td&gt;&#xA;          &lt;td&gt;To be written on the user project side&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;em&gt;Business example&lt;/em&gt;: “the booking date must be future and &amp;lt; 1 year”&lt;/td&gt;&#xA;          &lt;td&gt;Business&lt;/td&gt;&#xA;          &lt;td&gt;To be written on the user project side&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;full-pattern&#34;&gt;Full pattern&lt;a class=&#34;anchor&#34; href=&#34;#full-pattern&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/ocarina/custom_invariants/testing/workers.py&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;from&lt;/span&gt; ocarina.dsl.invariants.assertions &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; is_not_zero, is_positive&#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;from&lt;/span&gt; ocarina.dsl.invariants.validate &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; FrameworkInvariantValidator&#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;&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_workers_amount_chain&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    chain: ValidationStartBlock[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    value: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&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;-&amp;gt;&lt;/span&gt; ValidationAssertBlock[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    msg &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Value Error: Number of workers must be at least 1 (got: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;value&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;).&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;return&lt;/span&gt; chain&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;assert_that(is_positive, msg&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;msg)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;assert_that(is_not_zero, msg&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;msg)&#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;&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;validate_workers_amount&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;*&lt;/span&gt;, workers_amount: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;, name: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&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;-&amp;gt;&lt;/span&gt; ValidationAssertBlock[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&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;&amp;#34;&amp;#34;Validate that workers amount is at least 1.&amp;#34;&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:#ff79c6&#34;&gt;return&lt;/span&gt; FrameworkInvariantValidator&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;create(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        workers_amount, name, _workers_amount_chain&#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;ol&gt;&#xA;&lt;li&gt;Private &lt;code&gt;_workers_amount_chain(chain, value) -&amp;gt; ValidationAssertBlock&lt;/code&gt; defines the &lt;em&gt;what&lt;/em&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Public &lt;code&gt;validate_workers_amount(*, workers_amount, name)&lt;/code&gt; is the API and delegates to &lt;code&gt;FrameworkInvariantValidator.create&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Caller in &lt;code&gt;TestSuite.run&lt;/code&gt;: &lt;code&gt;validate_workers_amount(...).execute().raise_if_invalid()&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;which-factory-for-which-case&#34;&gt;Which factory for which case&lt;a class=&#34;anchor&#34; href=&#34;#which-factory-for-which-case&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Holy Book (excerpt from the &amp;ldquo;Extensibility&amp;rdquo; chapter):&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.05.05 — Worker saturation</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/05-orchestration/05-saturation/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/05-orchestration/05-saturation/</guid>
      <description>&lt;h1 id=&#34;020505worker-saturation&#34;&gt;02.05.05 — Worker saturation&lt;a class=&#34;anchor&#34; href=&#34;#020505worker-saturation&#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;Ocarina-specific trick: if a suite has fewer tests than workers, &lt;strong&gt;tests get randomly cloned&lt;/strong&gt; until the worker count is filled. Copies are renamed &lt;code&gt;[COPY 1] &amp;lt;name&amp;gt;&lt;/code&gt;, &lt;code&gt;[COPY 2] &amp;lt;name&amp;gt;&lt;/code&gt;, etc.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;why&#34;&gt;Why&lt;a class=&#34;anchor&#34; href=&#34;#why&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;The Holy Book formalizes the rationale in the &amp;ldquo;First real-world hurdles&amp;rdquo; chapter:&lt;/p&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Its &lt;code&gt;saturate_workers&lt;/code&gt; option lets you force random cloning of tests inside a suite.&lt;/p&gt;&#xA;&lt;p&gt;As soon as there are more available &lt;em&gt;workers&lt;/em&gt; in the &lt;em&gt;DriversPool&lt;/em&gt; than there are tests to run in a suite, Ocarina will randomly clone tests, start every driver, and assign each one a test to perform.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.10.05 — Selenium adapters</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/10-infra/05-selenium-adapters/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/10-infra/05-selenium-adapters/</guid>
      <description>&lt;h1 id=&#34;021005selenium-adapters&#34;&gt;02.10.05 — Selenium adapters&lt;a class=&#34;anchor&#34; href=&#34;#021005selenium-adapters&#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;Source folder: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/tree/main/src/ocarina/infra/selenium&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/infra/selenium/&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Everything that materializes the abstractions (&lt;code&gt;POMBase&lt;/code&gt;, &lt;code&gt;WebDriversPool&lt;/code&gt;, &lt;code&gt;Screenshotter&lt;/code&gt;) in Selenium flavor. &lt;strong&gt;Lives outside the pure DSL&lt;/strong&gt; — and since &lt;code&gt;1.1.3&lt;/code&gt; it has a twin: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/tree/main/src/ocarina/infra/playwright&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/infra/playwright/&lt;/code&gt;&lt;/a&gt; ships the exact same contracts in Playwright flavor. Swapping backend is now picking a folder, not writing one.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;files-in-the-folder&#34;&gt;Files in the folder&lt;a class=&#34;anchor&#34; href=&#34;#files-in-the-folder&#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;File&lt;/th&gt;&#xA;          &lt;th&gt;Role&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;create_driver.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;_build_firefox&lt;/code&gt;, &lt;code&gt;_build_chrome&lt;/code&gt;, &lt;code&gt;_build_edge&lt;/code&gt;, &lt;code&gt;_build_safari&lt;/code&gt; + dispatch&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;create_drivers_pool.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;create_selenium_drivers_pool&lt;/code&gt; (uses &lt;code&gt;DriverBuilder&lt;/code&gt; + &lt;code&gt;WebDriversPool&lt;/code&gt;)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;create_screenshotter.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;create_selenium_screenshotter&lt;/code&gt; + &lt;code&gt;_selenium_save_full_page&lt;/code&gt; (Firefox-only)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;driver_healthcheck.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;driver_healthcheck(driver)&lt;/code&gt; — pings &lt;code&gt;driver.title&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;mixins.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;SeleniumTitleMixin&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;create_driverpy&#34;&gt;&lt;code&gt;create_driver.py&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#create_driverpy&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_build_firefox&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, profile_path, driver_path, headless, wait_timeout) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; WebDriver:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    service &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; FirefoxService(executable_path&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;driver_path)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    options &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; FirefoxOptions()&#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; headless:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        options&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;add_argument(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;-headless&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;if&lt;/span&gt; profile_path:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        options&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;profile &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; FirefoxProfile(profile_path)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    driver &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Firefox(service&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;service, options&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;options)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    driver&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;implicitly_wait(wait_timeout)&#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;return&lt;/span&gt; driver&#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;&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_build_chrome&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, profile_path, driver_path, headless, wait_timeout) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; WebDriver:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    service &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; ChromeService(executable_path&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;driver_path)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    options &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; ChromeOptions()&#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; headless:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        options&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;add_argument(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;--headless=new&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;if&lt;/span&gt; profile_path:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        options&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;add_argument(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;--user-data-dir=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;profile_path&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    driver &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Chrome(service&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;service, options&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;options)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    driver&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;implicitly_wait(wait_timeout)&#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;return&lt;/span&gt; driver&#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;&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_build_edge&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, profile_path, driver_path, headless, wait_timeout) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; WebDriver:&#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;# … same as Chrome with EdgeService/EdgeOptions/Edge …&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;&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_build_safari&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, profile_path, driver_path, headless, wait_timeout) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; WebDriver:&#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;# … Safari supports neither driver_path (uses macOS&amp;#39; native safaridriver),&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;#   nor profile_path (no custom profile), nor headless …&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;1-implicitly_wait-set-only-once&#34;&gt;1. &lt;code&gt;implicitly_wait&lt;/code&gt; set only once&lt;a class=&#34;anchor&#34; href=&#34;#1-implicitly_wait-set-only-once&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;Each builder calls &lt;code&gt;driver.implicitly_wait(wait_timeout)&lt;/code&gt; &lt;em&gt;once&lt;/em&gt;. Any &lt;code&gt;find_element&lt;/code&gt; that doesn&amp;rsquo;t find the element immediately waits up to &lt;code&gt;wait_timeout&lt;/code&gt; seconds before raising &lt;code&gt;NoSuchElementException&lt;/code&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.11.05 — Report plugins</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/05-plugins-reports/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/05-plugins-reports/</guid>
      <description>&lt;h1 id=&#34;021105report-plugins&#34;&gt;02.11.05 — Report plugins&lt;a class=&#34;anchor&#34; href=&#34;#021105report-plugins&#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;Source folder: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/tree/main/src/ocarina/opinionated/plugins/reports&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/opinionated/plugins/reports/&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;pretty_print_results&lt;/code&gt;, &lt;code&gt;results_to_json&lt;/code&gt;, &lt;code&gt;generate_docx_proof&lt;/code&gt;, &lt;code&gt;timing&lt;/code&gt;. All opt-in. Run sequentially or in parallel via &lt;code&gt;run_plugins&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;pretty_print_results&#34;&gt;&lt;code&gt;pretty_print_results&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#pretty_print_results&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/ocarina/opinionated/plugins/reports/pretty_print_results.py&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;pretty_print_results&lt;/span&gt;(results: TestCycleResults, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, with_colors: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;False&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;&amp;#34;&amp;#34;Display the full test results.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Campaign&#xA;• Suite&#xA;  &amp;gt; Test case 1&#xA;    » PASSED&#xA;  &amp;gt; Test case 2&#xA;    » FAILED&#xA;      → Error message&#xA;        ⫸ At step 3&#xA;  &amp;gt; Test case 3&#xA;    » SKIPPED&#xA;&#xA;Test results:&#xA;1 FAILED | 1 PASSED | 1 SKIPPED&lt;/code&gt;&lt;/pre&gt;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Aspect&lt;/th&gt;&#xA;          &lt;th&gt;Detail&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;Hierarchy&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;3 levels: campaign &lt;code&gt;•&lt;/code&gt;, suite &lt;code&gt;&amp;gt;&lt;/code&gt;, case &lt;code&gt;»&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Levels&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;PASSED&lt;/code&gt; (green), &lt;code&gt;FAILED&lt;/code&gt; (red), &lt;code&gt;SKIPPED&lt;/code&gt; (gray)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Error&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Message + step number where it failed (&amp;quot;&lt;em&gt;At step 3&lt;/em&gt;&amp;quot;)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Aggregated count at the bottom&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;results_to_json&#34;&gt;&lt;code&gt;results_to_json&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#results_to_json&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;generate_json_results&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, results: TestCycleResults, output_dir: Path, logger: ILogger) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;&amp;#34;&amp;#34;Write test results to a JSON file in results_dir.&amp;#34;&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;    output_dir&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;mkdir(parents&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&lt;/span&gt;, exist_ok&lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;    max_stem_length &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    max_attempts &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;500&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;for&lt;/span&gt; _ &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;range&lt;/span&gt;(max_attempts):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        filename &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;uuid&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;uuid4()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;hex[:max_stem_length]&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;.json&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        file_path &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; output_dir &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; filename&#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; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; file_path&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;exists():&#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;break&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;else&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;raise&lt;/span&gt; RuntimeError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Can&amp;#39;t generate unique JSON filename in: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;output_dir&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;.&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&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-json&#34; data-lang=&#34;json&#34;&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;&amp;#34;Campaign&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;&amp;#34;Suite&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;&amp;#34;test_case&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;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;success&amp;#34;&lt;/span&gt; | &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;fail&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;error&amp;#34;&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;        counter,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        metadata&#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;    }&#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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_result_to_serializable&lt;/span&gt;(v: Any) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;dict&lt;/span&gt;[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&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;if&lt;/span&gt; is_ok(v):&#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;return&lt;/span&gt; {&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;success&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;if&lt;/span&gt; is_fail(v):&#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;return&lt;/span&gt; {&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;fail&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;(v&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;error)}&#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;raise&lt;/span&gt; TypeError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Expected Ok or Fail instances, but got: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt;(v)&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Filename is an 8-char hex UUID → 16⁸ ≈ 4 billion options. No collisions in practice. 500 safety retries.&lt;/p&gt;</description>
    </item>
    <item>
      <title>03.05 — Declarative programming</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/05-declarative/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/05-declarative/</guid>
      <description>&lt;h1 id=&#34;0305declarative-programming&#34;&gt;03.05 — Declarative programming&lt;a class=&#34;anchor&#34; href=&#34;#0305declarative-programming&#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;An Ocarina scenario &lt;strong&gt;describes&lt;/strong&gt;, it doesn&amp;rsquo;t execute. That&amp;rsquo;s what makes it factorable, multipliable, readable.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;demonstration&#34;&gt;Demonstration&lt;a class=&#34;anchor&#34; href=&#34;#demonstration&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    drive_page(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        act(on_homepage, open_then_verify_homepage)&#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;.&lt;/span&gt;failure(just_log_error(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Failed to reach the homepage...&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;.&lt;/span&gt;success(log_success_with_current_url_and_take_screenshot(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;On the homepage!&amp;#34;&lt;/span&gt;)),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        act(on_homepage, click_book_call_page_cta)&#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;.&lt;/span&gt;failure(just_log_error(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Failed to click on the &amp;#39;Book a call&amp;#39; CTA...&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;.&lt;/span&gt;success(just_log_success(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Clicked on the &amp;#39;Book a call&amp;#39; CTA!&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;    drive_page(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        act(on_book_a_call_page, verify_book_call_page)&#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;.&lt;/span&gt;failure(just_log_error(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Failed to verify the &amp;#39;Book a call&amp;#39; page...&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;.&lt;/span&gt;success(log_success_with_current_url_and_take_screenshot(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;On the &amp;#39;Book a call&amp;#39; page!&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This code &lt;strong&gt;executes nothing&lt;/strong&gt;. It &lt;strong&gt;describes&lt;/strong&gt;:&lt;/p&gt;</description>
    </item>
    <item>
      <title>04.05 — Snapshot testing (syrupy)</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/04-internal-tests/05-syrupy-snapshots/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/04-internal-tests/05-syrupy-snapshots/</guid>
      <description>&lt;h1 id=&#34;0405snapshot-testing-syrupy&#34;&gt;04.05 — Snapshot testing (&lt;code&gt;syrupy&lt;/code&gt;)&lt;a class=&#34;anchor&#34; href=&#34;#0405snapshot-testing-syrupy&#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;&lt;code&gt;syrupy&lt;/code&gt; is a pytest plugin that serializes a test&amp;rsquo;s output into a &lt;code&gt;.ambr&lt;/code&gt; file and compares to subsequent runs. Used for the output of &lt;code&gt;pretty_print_results&lt;/code&gt; and &lt;code&gt;results_to_json&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;ambr&#34;&gt;&lt;code&gt;.ambr&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#ambr&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;tests/opinionated/plugins/reports/__snapshots__/&#xA;├── test_pretty_print_results.ambr&#xA;└── test_results_to_json.ambr&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Generated and managed by &lt;code&gt;syrupy&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;example&#34;&gt;Example&lt;a class=&#34;anchor&#34; href=&#34;#example&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# tests/opinionated/plugins/reports/test_pretty_print_results.py&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;test_pretty_print_results_renders_campaign_suite_test&lt;/span&gt;(snapshot, capsys):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    results &lt;span style=&#34;color:#ff79c6&#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:#f1fa8c&#34;&gt;&amp;#34;Dashboard&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;Login happy paths&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;Login - without OTP&amp;#34;&lt;/span&gt;: (Ok(&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;), &lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;login_no_otp&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;Login - with OTP&amp;#34;&lt;/span&gt;: (Fail(error&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;RuntimeError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;OTP missed&amp;#34;&lt;/span&gt;)), &lt;span style=&#34;color:#bd93f9&#34;&gt;8&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;login_otp&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;Skipped one&amp;#34;&lt;/span&gt;: (&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;skipped_one&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;        },&#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;    pretty_print_results(results, with_colors&lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;    out &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; capsys&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;readouterr()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;out&#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;assert&lt;/span&gt; out &lt;span style=&#34;color:#ff79c6&#34;&gt;==&lt;/span&gt; snapshot&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;snapshot&lt;/code&gt; (&lt;code&gt;syrupy&lt;/code&gt; fixture):&lt;/p&gt;</description>
    </item>
    <item>
      <title>05.05 — wireit pipelines</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/05-igoristan/05-wireit/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/05-igoristan/05-wireit/</guid>
      <description>&lt;h1 id=&#34;0505wireit-pipelines&#34;&gt;05.05 — &lt;code&gt;wireit&lt;/code&gt; pipelines&lt;a class=&#34;anchor&#34; href=&#34;#0505wireit-pipelines&#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;&lt;code&gt;wireit&lt;/code&gt; is an NPM script orchestrator with incremental cache. Igoristan uses it to &lt;em&gt;chain&lt;/em&gt; &lt;code&gt;format-check&lt;/code&gt;, &lt;code&gt;lint&lt;/code&gt;, &lt;code&gt;typecheck&lt;/code&gt;, &lt;code&gt;prebuild&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;preview&lt;/code&gt;, all with fine per-task caching.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;packagejsonwireit&#34;&gt;&lt;code&gt;package.json#wireit&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#packagejsonwireit&#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-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;wireit&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;&amp;#34;ci:format-check&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;&amp;#34;command&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;prettier . --check&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;&amp;#34;files&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;*.{js,ts,jsx,tsx,json,md,mdx,html,css,scss,yml,yaml}&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;&amp;#34;output&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:#ff79c6&#34;&gt;&amp;#34;clean:dist&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;&amp;#34;command&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;rimraf dist&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;&amp;#34;files&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;dist/&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;&amp;#34;output&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:#ff79c6&#34;&gt;&amp;#34;prebuild&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;&amp;#34;files&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;src/**/*.*&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;package.json&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;packages/**/*.*&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;packages/**/package.json&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;vite.config.ts&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;&amp;#34;dependencies&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;lint&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;typecheck&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:#ff79c6&#34;&gt;&amp;#34;preview&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;&amp;#34;command&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;vike preview&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;&amp;#34;dependencies&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;build&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:#ff79c6&#34;&gt;&amp;#34;dev&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;&amp;#34;command&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;vike dev&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;&amp;#34;files&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;src/**/*.*&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;packages/**/*.*&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;&amp;#34;dependencies&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;prebuild&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;&amp;#34;output&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;dist/&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;&amp;#34;packageLocks&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;pnpm-lock.yaml&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:#ff79c6&#34;&gt;&amp;#34;build&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;&amp;#34;command&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;vike build&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;&amp;#34;files&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;src/**/*.*&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;packages/**/*.*&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;&amp;#34;dependencies&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;prebuild&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;&amp;#34;output&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;dist/&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;&amp;#34;packageLocks&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;pnpm-lock.yaml&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:#ff79c6&#34;&gt;&amp;#34;lint&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;&amp;#34;command&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;eslint \&amp;#34;./{src,packages}/**/*.{js,jsx,ts,tsx}\&amp;#34; --max-warnings 0 --cache&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;&amp;#34;files&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;src/**/*.*&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;package.json&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;packages/**/*.*&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;eslint.config.ts&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;&amp;#34;output&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:#ff79c6&#34;&gt;&amp;#34;typecheck&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;&amp;#34;command&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;tsc --build --pretty tsconfig.json&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;&amp;#34;files&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;src/**/*.*&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;package.json&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;tsconfig.app.json&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;tsconfig.json&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;tsconfig.node.json&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;&amp;#34;output&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;typecheck-dist/&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;dag&#34;&gt;DAG&lt;a class=&#34;anchor&#34; href=&#34;#dag&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;                 ┌─────────────────┐&#xA;                 │ ci:format-check │ ────► prettier . --check&#xA;                 └─────────────────┘&#xA;                 ┌─────────────────┐&#xA;                 │     lint        │ ────► eslint (max-warnings 0, --cache)&#xA;                 └─────┬───────────┘&#xA;                       │&#xA;                 ┌─────▼───────────┐&#xA;                 │   typecheck     │ ────► tsc --build --pretty&#xA;                 └─────┬───────────┘&#xA;                       │&#xA;                 ┌─────▼───────────┐&#xA;                 │   prebuild      │ ────► deps: [lint, typecheck]&#xA;                 └─────┬───────────┘&#xA;                       │&#xA;            ┌──────────┴──────────┐&#xA;            ▼                     ▼&#xA;   ┌─────────────────┐    ┌─────────────────┐&#xA;   │   build         │    │   dev           │&#xA;   │   vike build    │    │   vike dev      │&#xA;   └────┬────────────┘    └─────────────────┘&#xA;        ▼&#xA;   ┌─────────────────┐&#xA;   │   preview       │&#xA;   │   vike preview  │&#xA;   └─────────────────┘&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With &lt;code&gt;pnpm dev&lt;/code&gt;:&lt;/p&gt;</description>
    </item>
    <item>
      <title>06.05 — isAuthorized.ts</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/06-tests-workers/05-is-authorized/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/06-tests-workers/05-is-authorized/</guid>
      <description>&lt;h1 id=&#34;0605isauthorizedts&#34;&gt;06.05 — &lt;code&gt;isAuthorized.ts&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#0605isauthorizedts&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/tests-workers/blob/main/lib/isAuthorized.ts&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;lib/isAuthorized.ts&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Verifies that a request contains the right &lt;code&gt;x-api-key&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;type&lt;/span&gt; { NextRequest } &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;next/server&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:#ff79c6&#34;&gt;const&lt;/span&gt; API_SECRET &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; process.env.API_SECRET;&#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:#8be9fd;font-style:italic&#34;&gt;function&lt;/span&gt; isAuthorized(request: &lt;span style=&#34;color:#8be9fd&#34;&gt;NextRequest&lt;/span&gt;)&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;boolean&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;if&lt;/span&gt; (&lt;span style=&#34;color:#ff79c6&#34;&gt;typeof&lt;/span&gt; API_SECRET &lt;span style=&#34;color:#ff79c6&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;string&amp;#34;&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    console.error(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;[CONFIG ERROR] API_SECRET is not defined&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;return&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;  }&#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;const&lt;/span&gt; { searchParams } &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; URL(request.url);&#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;const&lt;/span&gt; apiKey &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; searchParams.&lt;span style=&#34;color:#ff79c6&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;apiKey&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;??&lt;/span&gt; request.headers.&lt;span style=&#34;color:#ff79c6&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;x-api-key&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;return&lt;/span&gt; apiKey &lt;span style=&#34;color:#ff79c6&#34;&gt;===&lt;/span&gt; API_SECRET;&#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;&#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;export&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;default&lt;/span&gt; isAuthorized;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;approach&#34;&gt;Approach&lt;a class=&#34;anchor&#34; href=&#34;#approach&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;1-api_secret&#34;&gt;1. &lt;code&gt;API_SECRET&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#1-api_secret&#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-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt; API_SECRET &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; process.env.API_SECRET;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Read once at Edge worker startup (cold start), stored as a module variable. Faster than hitting &lt;code&gt;process.env&lt;/code&gt; on every call.&lt;/p&gt;</description>
    </item>
    <item>
      <title>07.05 — Randomness scenarios (4 levels)</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/05-scenarios-randomness/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/05-scenarios-randomness/</guid>
      <description>&lt;h1 id=&#34;0705randomness-scenarios-4-levels&#34;&gt;07.05 — Randomness scenarios (4 levels)&lt;a class=&#34;anchor&#34; href=&#34;#0705randomness-scenarios-4-levels&#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;Four progressive levels of chaos. We start simple, ramp up complexity, stress Ocarina more and more.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;campaign&#34;&gt;Campaign&lt;a class=&#34;anchor&#34; href=&#34;#campaign&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/tests/campaigns/randomness.py&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;create_igoristan_randomness_campaign&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, drivers_pool) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; TestCampaign:&#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;return&lt;/span&gt; TestCampaign(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Randomness&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        suites&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;[create_randomness_test_suite(drivers_pool&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;drivers_pool)],&#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;h2 id=&#34;suite&#34;&gt;Suite&lt;a class=&#34;anchor&#34; href=&#34;#suite&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/tests/suites/randomness.py&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;create_randomness_test_suite&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, drivers_pool) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; TestSuite:&#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;return&lt;/span&gt; TestSuite(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Randomness&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        tests&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;[&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            test_random_error_page,                     &lt;span style=&#34;color:#6272a4&#34;&gt;# level 1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            test_random_loaders_page,                   &lt;span style=&#34;color:#6272a4&#34;&gt;# level 1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            test_dsed,                                  &lt;span style=&#34;color:#6272a4&#34;&gt;# level 2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            test_madness,                               &lt;span style=&#34;color:#6272a4&#34;&gt;# level 2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            test_chaotic_form,                          &lt;span style=&#34;color:#6272a4&#34;&gt;# level 3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            test_walkthrough,                           &lt;span style=&#34;color:#6272a4&#34;&gt;# level 4&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;        drivers_pool&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;drivers_pool,&#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;h2 id=&#34;levels&#34;&gt;Levels&lt;a class=&#34;anchor&#34; href=&#34;#levels&#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;Level&lt;/th&gt;&#xA;          &lt;th&gt;Mechanic exercised&lt;/th&gt;&#xA;          &lt;th&gt;Difficulty&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;Base DSL (&lt;code&gt;drive_page&lt;/code&gt;, &lt;code&gt;act&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;easy&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;2&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;match_page&lt;/code&gt; / &lt;code&gt;when&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;medium&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;3&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;HumanizedDriver&lt;/code&gt; + &lt;code&gt;Watcher&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;hard&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;4&lt;/td&gt;&#xA;          &lt;td&gt;Multi-page chaotic walkthrough&lt;/td&gt;&#xA;          &lt;td&gt;hard&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;level-1random-error-page&#34;&gt;Level 1 — Random Error Page&lt;a class=&#34;anchor&#34; href=&#34;#level-1random-error-page&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# tests/scenarios/randomness/level_1/random_error_page.py&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;scenario_random_error_page&lt;/span&gt;(driver, logger):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; RandomErrorPage(driver&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;driver)&#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;return&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        drive_page(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(page, open_random_error_page)&lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(page, verify_random_error_page)&lt;span style=&#34;color:#ff79c6&#34;&gt;...&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;    ]&#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;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test_random_error_page &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; create_selenium_test(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Random Error Page - smoke&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    test_scenario&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt; driver, logger: Scenario(test_chain&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;scenario_random_error_page(driver, logger)),&#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;Deliberately flaky — exercises the &lt;code&gt;on_failure&lt;/code&gt; hook.&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>09.03.05 — Pick skills</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/05-pick/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/05-pick/</guid>
      <description>&lt;h1 id=&#34;090305pick-skills&#34;&gt;09.03.05 — Pick skills&lt;a class=&#34;anchor&#34; href=&#34;#090305pick-skills&#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;Picking artifacts (screenshots, logs, reports). &lt;strong&gt;Always by mtime, never by filename.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;listing-potentially-non-exhaustive&#34;&gt;Listing (potentially non-exhaustive)&lt;a class=&#34;anchor&#34; href=&#34;#listing-potentially-non-exhaustive&#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;Skill&lt;/th&gt;&#xA;          &lt;th&gt;Target&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;pick-screenshots&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Picks the most recent screenshots (by mtime), contextualizing them with logs where possible&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;pick-logs&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Picks the most recent logs (by mtime)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;pick-reports&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Picks the most recent DOCX/JSON reports (by mtime)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;mtime&#34;&gt;&amp;ldquo;&lt;em&gt;mtime&lt;/em&gt;&amp;rdquo;&lt;a class=&#34;anchor&#34; href=&#34;#mtime&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Holy Book quote:&lt;/p&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;&lt;strong&gt;Mtime, not filename.&lt;/strong&gt; UUID suffixes are random; &lt;code&gt;pick-*&lt;/code&gt; sorts by mtime.&lt;/p&gt;</description>
    </item>
    <item>
      <title>09.05 — PDF generation (generate-books)</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/05-pdf-generation/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/05-pdf-generation/</guid>
      <description>&lt;h1 id=&#34;0905pdf-generation-generate-books&#34;&gt;09.05 — PDF generation (&lt;code&gt;generate-books&lt;/code&gt;)&lt;a class=&#34;anchor&#34; href=&#34;#0905pdf-generation-generate-books&#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 public PDFs (&lt;code&gt;ocarina-ru.pdf&lt;/code&gt;, &lt;code&gt;ocarina-en.pdf&lt;/code&gt;, &lt;code&gt;ocarina-fr.pdf&lt;/code&gt;&amp;hellip;) are &lt;strong&gt;AI-generated&lt;/strong&gt; from the &lt;code&gt;docs/&lt;/code&gt; folder, a Python script, and a Markdown prompt.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;tree&#34;&gt;Tree&lt;a class=&#34;anchor&#34; href=&#34;#tree&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;prompts/generate-books/&#xA;├── prompt.md                     # spec (read by the AI)&#xA;├── script.py                     # Python implementation&#xA;├── NotoSans-Regular.ttf&#xA;├── NotoSans-Bold.ttf&#xA;├── NotoSans-Italic.ttf&#xA;├── NotoSans-BoldItalic.ttf&#xA;├── NotoSansMath-Regular.ttf&#xA;├── NotoSansMono-Regular.ttf&#xA;├── NotoSansSymbols2-Regular.ttf&#xA;├── NotoNaskhArabic-Regular.ttf&#xA;└── NotoColorEmoji-Regular.ttf&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;1-a-promptmd--a-scriptpy&#34;&gt;1. A &lt;code&gt;prompt.md&lt;/code&gt; + a &lt;code&gt;script.py&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#1-a-promptmd--a-scriptpy&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;prompt.md&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;h1 id=&#34;ocarina-pdf-generator&#34;&gt;Ocarina PDF Generator&lt;a class=&#34;anchor&#34; href=&#34;#ocarina-pdf-generator&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;what-this-is&#34;&gt;What this is&lt;a class=&#34;anchor&#34; href=&#34;#what-this-is&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;A system that turns a VitePress &lt;code&gt;docs/&lt;/code&gt; folder (Markdown + images) into three polished A4 PDFs — one Russian, one English, one French. The system is two files: this prompt (the spec) and &lt;code&gt;script.py&lt;/code&gt; (the implementation). They are designed to be used together in an agentic environment.&lt;/p&gt;</description>
    </item>
    <item>
      <title>10.05 — igoristan workflows</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/05-igoristan-workflows/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/05-igoristan-workflows/</guid>
      <description>&lt;h1 id=&#34;1005igoristan-workflows&#34;&gt;10.05 — &lt;code&gt;igoristan&lt;/code&gt; workflows&lt;a class=&#34;anchor&#34; href=&#34;#1005igoristan-workflows&#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;See also &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/05-igoristan/07-ci-deploy/&#34;&gt;&lt;code&gt;../05-igoristan/07-ci-deploy.md&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;a class=&#34;anchor&#34; href=&#34;#overview&#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;Workflow&lt;/th&gt;&#xA;          &lt;th&gt;Trigger&lt;/th&gt;&#xA;          &lt;th&gt;OS&lt;/th&gt;&#xA;          &lt;th&gt;Effect&lt;/th&gt;&#xA;          &lt;th&gt;Note&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;ci-pr.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PR &lt;code&gt;**&lt;/code&gt;, dispatch&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu × node 24 + pnpm 10&lt;/td&gt;&#xA;          &lt;td&gt;3 parallelized jobs: &lt;code&gt;wireit ci:format-check&lt;/code&gt; + &lt;code&gt;wireit lint&lt;/code&gt; + &lt;code&gt;wireit typecheck&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PR gate&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;deploy.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;push main, dispatch&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu × node 24 + pnpm 10&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;pnpm install --frozen-lockfile&lt;/code&gt; + &lt;code&gt;pnpm build&lt;/code&gt; + upload Pages&lt;/td&gt;&#xA;          &lt;td&gt;Production&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;ci-pryml&#34;&gt;&lt;code&gt;ci-pr.yml&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#ci-pryml&#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;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;format-check&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;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;WIREIT_CACHE&lt;/span&gt;: github&#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@v6&#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;: pnpm/action-setup@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;: { &lt;span style=&#34;color:#ff79c6&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;11&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/setup-node@v6&#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;: { &lt;span style=&#34;color:#ff79c6&#34;&gt;node-version: 24, cache&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;pnpm&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;uses&lt;/span&gt;: google/wireit@setup-github-actions-caching/v2&#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;: make install&#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;: Formatcheck&#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;: make ci:format-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;lint&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;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;WIREIT_CACHE&lt;/span&gt;: github&#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:#6272a4&#34;&gt;# ... same setup ...&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@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;path&lt;/span&gt;: .eslintcache&#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;: eslint-cache-${{ hashFiles(&amp;#39;eslint.config.ts&amp;#39;, &amp;#39;src/**/*.*&amp;#39;, &amp;#39;package.json&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;: 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;: pnpm lint&#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;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;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;WIREIT_CACHE&lt;/span&gt;: github&#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:#6272a4&#34;&gt;# ... same setup ...&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;: Typecheck&#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;: pnpm typecheck&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;Job&lt;/th&gt;&#xA;          &lt;th&gt;Additional cache&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;format-check&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;wireit&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;lint&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;wireit + &lt;code&gt;.eslintcache&lt;/code&gt; (actions/cache)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;typecheck&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;wireit + &lt;code&gt;tsbuildinfo&lt;/code&gt; (via wireit)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;wireit_cache-github&#34;&gt;&lt;code&gt;WIREIT_CACHE: github&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#wireit_cache-github&#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;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;WIREIT_CACHE&lt;/span&gt;: github&#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;: google/wireit@setup-github-actions-caching/v2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Enables the &lt;em&gt;wireit&lt;/em&gt; cache. Subsequent runs reuse wireit artifacts.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.05 — Early-2000s Indonesian hackers</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/05-indonesian-hackers/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/05-indonesian-hackers/</guid>
      <description>&lt;h1 id=&#34;1205early-2000s-indonesian-hackers&#34;&gt;12.05 — Early-2000s Indonesian hackers&lt;a class=&#34;anchor&#34; href=&#34;#1205early-2000s-indonesian-hackers&#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 Holy Book quotes &amp;ldquo;&lt;em&gt;the first Indonesian hacker group&lt;/em&gt;&amp;rdquo; without naming it in the main text, but the cited mantra (&amp;quot;&lt;em&gt;We Can Do All What You Can&amp;rsquo;t Do&lt;/em&gt;&amp;quot;) is indeed &lt;strong&gt;YogyaCarderLink (YCL)&lt;/strong&gt;&amp;rsquo;s. It remains to explain &lt;em&gt;why&lt;/em&gt; the Indonesian scene of that era deserves a citation in a 2026 testing framework manifesto.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;1-the-may-1998-riots&#34;&gt;1. The May 1998 riots&lt;a class=&#34;anchor&#34; href=&#34;#1-the-may-1998-riots&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;the-trigger&#34;&gt;The trigger&lt;a class=&#34;anchor&#34; href=&#34;#the-trigger&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;In May 1998, Indonesia went through a major economic crisis (fallout from the 1997 Asian crisis) and a political crisis (end of the Suharto regime). Riots broke out in Jakarta, Medan, Surakarta, notably targeting the &lt;strong&gt;Indonesian Chinese minority&lt;/strong&gt; (merchants, middle class). Estimates: ~1000 dead, hundreds of documented rapes.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.10.06 — The Playwright actor: one owner thread</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/10-infra/06-playwright-actor/</link>
      <pubDate>Mon, 01 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/10-infra/06-playwright-actor/</guid>
      <description>&lt;h1 id=&#34;021006the-playwright-actor-one-owner-thread&#34;&gt;02.10.06 — The Playwright actor: one owner thread&lt;a class=&#34;anchor&#34; href=&#34;#021006the-playwright-actor-one-owner-thread&#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;Source folder: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/tree/main/src/ocarina/infra/playwright&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/infra/playwright/&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;The Playwright adapter mirrors the Selenium one file for file (&lt;code&gt;create_driver&lt;/code&gt;, &lt;code&gt;create_drivers_pool&lt;/code&gt;, &lt;code&gt;create_screenshotter&lt;/code&gt;, &lt;code&gt;driver_healthcheck&lt;/code&gt;, &lt;code&gt;mixins&lt;/code&gt;), with &lt;strong&gt;one extra file&lt;/strong&gt;: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/infra/playwright/driver.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;driver.py&lt;/code&gt;&lt;/a&gt;. That file deserves a chapter of its own. It is what reconciles Playwright&amp;rsquo;s &lt;em&gt;sync&lt;/em&gt; API — inherently bound to one thread — with Ocarina&amp;rsquo;s threaded model (pool, warmup, Watcher). The model has been stressed and stabilized; it holds.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;the-problem-playwrights-sync-api-is-thread-affine&#34;&gt;The problem: Playwright&amp;rsquo;s sync API is thread-affine&lt;a class=&#34;anchor&#34; href=&#34;#the-problem-playwrights-sync-api-is-thread-affine&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Playwright&amp;rsquo;s &lt;strong&gt;sync&lt;/strong&gt; API binds every object it hands out — &lt;code&gt;Playwright&lt;/code&gt;, &lt;code&gt;Browser&lt;/code&gt;, &lt;code&gt;BrowserContext&lt;/code&gt;, &lt;code&gt;Page&lt;/code&gt;, &lt;code&gt;Locator&lt;/code&gt;, … — to the thread that called &lt;code&gt;sync_playwright().start()&lt;/code&gt;. Under the hood it is a &lt;em&gt;greenlet&lt;/em&gt; pinned to that thread. Touching any of them from another thread raises:&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.03.06 — drive_page</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/03-railway/06-drive-page/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/03-railway/06-drive-page/</guid>
      <description>&lt;h1 id=&#34;020306drive_page&#34;&gt;02.03.06 — &lt;code&gt;drive_page&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#020306drive_page&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/dsl/drive_page.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/opinionated/dsl/drive_page.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;drive_page&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    first: ActionSuccess[TPOM], &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;rest: ActionSuccess[TPOM]&#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;-&amp;gt;&lt;/span&gt; ChainRunner[TPOM]:&#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;return&lt;/span&gt; chain_actions(first, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;rest)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Literally&lt;/strong&gt; &lt;code&gt;chain_actions&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-this-alias&#34;&gt;Why this alias?&lt;a class=&#34;anchor&#34; href=&#34;#why-this-alias&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;1-semantics-im-taking-control-of-a-page&#34;&gt;1. Semantics: &amp;ldquo;I&amp;rsquo;m taking control of a page&amp;rdquo;&lt;a class=&#34;anchor&#34; href=&#34;#1-semantics-im-taking-control-of-a-page&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;Holy Book quote (&amp;ldquo;First scenarios&amp;rdquo;):&lt;/p&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;&lt;code&gt;drive_page&lt;/code&gt; expresses the fact that you are taking control of &lt;em&gt;one&lt;/em&gt; page.&#xA;Any &lt;em&gt;transition&lt;/em&gt; becomes explicit through the opening of a new &lt;code&gt;drive_page&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    drive_page(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        act(on_homepage, open_homepage)&lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        act(on_homepage, verify_homepage)&lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        act(on_homepage, click_cta)&lt;span style=&#34;color:#ff79c6&#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;# ⬅ closing control of the homepage&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    drive_page(                                 &lt;span style=&#34;color:#6272a4&#34;&gt;# ⬅ opening control of the next page&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        act(on_target_page, verify_target_page)&lt;span style=&#34;color:#ff79c6&#34;&gt;...&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;2-discipline&#34;&gt;2. Discipline&lt;a class=&#34;anchor&#34; href=&#34;#2-discipline&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;Mixing acts from two different POMs inside one &lt;code&gt;drive_page&lt;/code&gt; is a &lt;em&gt;mypy&lt;/em&gt; error (see &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/03-railway/02-action-chain-states/&#34;&gt;&lt;code&gt;02-action-chain-states.md&lt;/code&gt;&lt;/a&gt;, &amp;ldquo;narrowing via &lt;code&gt;act()&lt;/code&gt;&amp;rdquo; section). The semantics get enforced concretely.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.04.06 — Invariant error hierarchy</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/04-invariants/06-invariant-errors/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/04-invariants/06-invariant-errors/</guid>
      <description>&lt;h1 id=&#34;020406invariant-error-hierarchy&#34;&gt;02.04.06 — Invariant error hierarchy&lt;a class=&#34;anchor&#34; href=&#34;#020406invariant-error-hierarchy&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/invariants/errors.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/invariants/errors.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;InvariantViolationError (base — subclass of Exception)&#xA;├── DuplicatesError                  (raised by `has_unique_elements`)&#xA;└── AggregateInvariantViolationError (raised by `_ValidationResult.raise_if_invalid`)&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;invariantviolationerror&#34;&gt;&lt;code&gt;InvariantViolationError&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#invariantviolationerror&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;InvariantViolationError&lt;/span&gt;(Exception):&#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;&amp;#34;&amp;#34;Base exception raised when an invariant is violated.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Bare &lt;code&gt;Exception&lt;/code&gt; subclass, no extra logic. Every predicate raises this (or a subclass).&lt;/p&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;try&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    validate(value)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;assert_that(predicate)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;raise_if_invalid()&#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;except&lt;/span&gt; InvariantViolationError &lt;span style=&#34;color:#ff79c6&#34;&gt;as&lt;/span&gt; e:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logger&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;error(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Validation failed: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;e&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;… catches &lt;strong&gt;every&lt;/strong&gt; violation with a single clause.&lt;/p&gt;&#xA;&lt;h2 id=&#34;duplicateserror&#34;&gt;&lt;code&gt;DuplicatesError&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#duplicateserror&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;DuplicatesError&lt;/span&gt;(InvariantViolationError):&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, duplicates: Sequence[Any], message: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;duplicates &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; duplicates&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        msg &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; message &lt;span style=&#34;color:#ff79c6&#34;&gt;or&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Duplicate elements detected:&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;join(&#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;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34; - &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;d&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; d &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; duplicates&#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:#8be9fd;font-style:italic&#34;&gt;super&lt;/span&gt;()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&lt;/span&gt;(msg)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;Stashes the duplicate list in &lt;code&gt;.duplicates&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Default message: &amp;ldquo;&lt;em&gt;Duplicate elements detected:&lt;code&gt;\n&lt;/code&gt; - apple&lt;code&gt;\n&lt;/code&gt; - banana&lt;/em&gt;&amp;rdquo;.&lt;/li&gt;&#xA;&lt;li&gt;Accepts a custom &lt;code&gt;message=&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Raised by &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/invariants/assertions.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;has_unique_elements&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.05.06 — TestCampaign[Driver]</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/05-orchestration/06-test-campaign/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/05-orchestration/06-test-campaign/</guid>
      <description>&lt;h1 id=&#34;020506testcampaigndriver&#34;&gt;02.05.06 — &lt;code&gt;TestCampaign[Driver]&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#020506testcampaigndriver&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/oc_test_campaign.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing/oc_test_campaign.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;One job&lt;/strong&gt;: run a &lt;strong&gt;sequence of suites&lt;/strong&gt; in order, share a workers config, and declare &lt;code&gt;campaign_has_failed&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;TestCampaign&lt;/span&gt;[Driver]:&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&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;font-style:italic&#34;&gt;self&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;*&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        name: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        suites: Sequence[TestSuite[Driver]],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        max_workers: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        saturate_workers: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        validate_test_suites_names(suites&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;suites, name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;suites&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;raise_if_invalid()&#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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_suites &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; suites&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_results: TestCampaignResults &lt;span style=&#34;color:#ff79c6&#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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_max_workers &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; max_workers&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_saturate_workers &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; saturate_workers&#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;for&lt;/span&gt; suite &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_suites:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            suite&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_campaign_name &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;run_all&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;font-style:italic&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, skip_all: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;False&lt;/span&gt;, saturate_workers: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;-&amp;gt;&lt;/span&gt; TestCampaignResults:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_results&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;clear()&#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;        resolved_saturate_workers &lt;span style=&#34;color:#ff79c6&#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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_saturate_workers&#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; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_saturate_workers &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;else&lt;/span&gt; saturate_workers&#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;&#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; skip_all:&#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;for&lt;/span&gt; suite &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_suites:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_results[suite&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    name: (&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;, test_id)&#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;for&lt;/span&gt; name, test_id &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; suite&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;test_names_and_ids&#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;return&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_results&#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;for&lt;/span&gt; suite &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_suites:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_results[suite&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; suite&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;run(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                max_workers&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_max_workers,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                saturate_workers&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;resolved_saturate_workers,&#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;&#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;return&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_results&#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;&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;campaign_has_failed&lt;/span&gt;(results: TestCampaignResults) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&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;return&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;any&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        is_test_result_fail(outcome)&#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;for&lt;/span&gt; campaign_results &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; results&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;values()&#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;for&lt;/span&gt; outcome, _, _ &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; campaign_results&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;values()&#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;h2 id=&#34;anatomy&#34;&gt;Anatomy&lt;a class=&#34;anchor&#34; href=&#34;#anatomy&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;constructor-guard&#34;&gt;Constructor guard&lt;a class=&#34;anchor&#34; href=&#34;#constructor-guard&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;validate_test_suites_names(suites&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;suites, name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;suites&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;raise_if_invalid()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;→ Suite names inside a campaign must be unique — case-insensitively since &lt;code&gt;1.1.10&lt;/code&gt; (NFC + casefold). Raised &lt;em&gt;at construction&lt;/em&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.06 — Scenario[Driver]</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/06-scenario/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/06-scenario/</guid>
      <description>&lt;h1 id=&#34;0206scenariodriver&#34;&gt;02.06 — &lt;code&gt;Scenario[Driver]&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#0206scenariodriver&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/custom_types/scenario.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/custom_types/scenario.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;dataclass&#34;&gt;Dataclass&lt;a class=&#34;anchor&#34; href=&#34;#dataclass&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@final&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@dataclass(frozen&lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;Scenario&lt;/span&gt;[Driver]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    test_chain: TestChain&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    setup: TestSetup &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; field(default&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    teardown: TestTeardown &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; field(default&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    watchers: TestWatchers[Driver] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; field(default&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/ocarina/custom_types/test_components.py&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:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; TestChain &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Sequence[ChainRunner[Any]]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; TestSetup &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Effect &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; TestTeardown &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Effect &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; TestWatchers[Driver] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Sequence[Watcher[Driver]] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;lifecycle-per-attempt&#34;&gt;Lifecycle (per attempt)&lt;a class=&#34;anchor&#34; href=&#34;#lifecycle-per-attempt&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;1. setup()           — optional, free-form Effect (DB, API, …)&#xA;       → raises    :  test_chain skipped, jump to teardown,&#xA;                      return Outcome(setup_failed=True, should_retry=True)&#xA;       → ok        :  continue to test_chain&#xA;&#xA;2. test_chain        — the actual chain (Sequence[ChainRunner])&#xA;       (watchers run during this time)&#xA;&#xA;3. teardown()        — optional, always executed&#xA;       → raises    :  log warning + ignore (does not affect the verdict)&#xA;&#xA;If EVERY attempt raises during setup:&#xA;    → test marked SKIPPED (not FAILED)&#xA;    → warning log « setup keeps failing »&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;setup-and-teardown&#34;&gt;&lt;code&gt;setup&lt;/code&gt; and &lt;code&gt;teardown&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#setup-and-teardown&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;setup&lt;/code&gt; and &lt;code&gt;teardown&lt;/code&gt; are &lt;strong&gt;driver-free and injection-free by design.&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.11.06 — bootstrap &#43; run_plugins</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/06-bootstrap-launcher/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/11-opinionated/06-bootstrap-launcher/</guid>
      <description>&lt;h1 id=&#34;021106bootstrap--run_plugins&#34;&gt;02.11.06 — &lt;code&gt;bootstrap&lt;/code&gt; + &lt;code&gt;run_plugins&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#021106bootstrap--run_plugins&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/opinionated/launcher/bootstrap.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/opinionated/launcher/bootstrap.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Entry point of an Ocarina project.&lt;br&gt;&#xA;Three steps: &lt;code&gt;cycle.run_all → run_plugins → post_exec&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;bootstrap&lt;/span&gt;[T](&#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;*&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    test_cycle: TestCycle[T],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    run_plugins: Callable[[TestCycleResults], &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    post_exec: Callable[[TestCycleResults], &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    saturate_workers: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    results &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; test_cycle&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;run_all(saturate_workers&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;saturate_workers)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    run_plugins(results)&#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; post_exec:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        post_exec(results)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;order&#34;&gt;Order&lt;a class=&#34;anchor&#34; href=&#34;#order&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;1. cycle.run_all()  →  results&#xA;2. run_plugins(results)&#xA;3. post_exec(results) (optional)&lt;/code&gt;&lt;/pre&gt;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Step&lt;/th&gt;&#xA;          &lt;th&gt;Effect&lt;/th&gt;&#xA;          &lt;th&gt;Why this order&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;All tests run&lt;/td&gt;&#xA;          &lt;td&gt;We need &lt;code&gt;results&lt;/code&gt; for the rest&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;2&lt;/td&gt;&#xA;          &lt;td&gt;Plugins fire (DOCX, JSON, etc.)&lt;/td&gt;&#xA;          &lt;td&gt;Must run &lt;em&gt;before&lt;/em&gt; &lt;code&gt;post_exec&lt;/code&gt; since it can take generation time&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;3&lt;/td&gt;&#xA;          &lt;td&gt;Post-exec (pretty_print + sys.exit)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Last&lt;/em&gt; thing — after this, the process is dead&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;typing&#34;&gt;Typing&lt;a class=&#34;anchor&#34; href=&#34;#typing&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;run_plugins: Callable[[TestCycleResults], &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;post_exec: Callable[[TestCycleResults], &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Both receive the &lt;strong&gt;&lt;code&gt;results&lt;/code&gt;&lt;/strong&gt; as an argument:&lt;/p&gt;</description>
    </item>
    <item>
      <title>03.06 — PEP 695 generics in Ocarina</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/06-pep-695-generics/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/06-pep-695-generics/</guid>
      <description>&lt;h1 id=&#34;0306pep-695-generics-in-ocarina&#34;&gt;03.06 — PEP 695 generics in Ocarina&lt;a class=&#34;anchor&#34; href=&#34;#0306pep-695-generics-in-ocarina&#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;Ocarina leans hard on the generics syntax from PEP 695 (Python 3.12+). It&amp;rsquo;s what keeps the whole typed ecosystem &lt;em&gt;from being a mess&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;three-forms-of-pep-695&#34;&gt;Three forms of PEP 695&lt;a class=&#34;anchor&#34; href=&#34;#three-forms-of-pep-695&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;1-generic-classes&#34;&gt;1. Generic classes&lt;a class=&#34;anchor&#34; href=&#34;#1-generic-classes&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@final&#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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;Ok&lt;/span&gt;[T](_BaseResult):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    value: T&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error: &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;TestSuite&lt;/span&gt;[Driver]:&#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;...&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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;Watcher&lt;/span&gt;[Driver]:&#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;...&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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;ChainRunner&lt;/span&gt;[T]:&#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;...&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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;ValidationStartBlock&lt;/span&gt;[T]:&#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;...&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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;CliStore&lt;/span&gt;[TKeys: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;]:  &lt;span style=&#34;color:#6272a4&#34;&gt;# with bound&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;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Before PEP 695:&lt;/p&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; Generic, TypeVar&#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;T &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; TypeVar(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;T&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:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;Ok&lt;/span&gt;(_BaseResult, Generic[T]):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    value: T&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error: &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;2-parameterized-type-aliases&#34;&gt;2. Parameterized type aliases&lt;a class=&#34;anchor&#34; href=&#34;#2-parameterized-type-aliases&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Result[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Ok[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; Fail&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Thunk[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Callable[[], T]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Action[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Thunk[Result[T]]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; FailureHandler &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Callable[[Exception], &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Predicate[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Callable[[T], &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Effect &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Callable[[], &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; TestScenario[Driver] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Callable[[Driver, ILogger], Scenario[Driver]]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; TestScenarioFragment[Driver] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Callable[[Driver, ILogger], TestChain]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; TestChain &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Sequence[ChainRunner[Any]]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; TestWatchers[Driver] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Sequence[Watcher[Driver]] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Before PEP 695:&lt;/p&gt;</description>
    </item>
    <item>
      <title>04.06 — Property-based testing (hypothesis)</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/04-internal-tests/06-hypothesis-properties/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/04-internal-tests/06-hypothesis-properties/</guid>
      <description>&lt;h1 id=&#34;0406property-based-testing-hypothesis&#34;&gt;04.06 — Property-based testing (&lt;code&gt;hypothesis&lt;/code&gt;)&lt;a class=&#34;anchor&#34; href=&#34;#0406property-based-testing-hypothesis&#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;A single file (&lt;code&gt;test_invariants_properties.py&lt;/code&gt;), but the pattern is interesting: we &lt;strong&gt;generate&lt;/strong&gt; inputs and verify &lt;strong&gt;universal properties&lt;/strong&gt; of the invariant predicates.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;the-concept&#34;&gt;The concept&lt;a class=&#34;anchor&#34; href=&#34;#the-concept&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;hypothesis&lt;/code&gt; auto-generates &lt;em&gt;test cases&lt;/em&gt; that satisfy given constraints, then checks a property holds for every generated case. On failure, &lt;em&gt;hypothesis&lt;/em&gt; does &lt;strong&gt;shrinking&lt;/strong&gt; to find the &lt;strong&gt;smallest counter-example&lt;/strong&gt;.&lt;/p&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; hypothesis &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; given, strategies &lt;span style=&#34;color:#ff79c6&#34;&gt;as&lt;/span&gt; st&#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;@given(st&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;integers())&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;test_is_positive_passes_on_positive&lt;/span&gt;(value):&#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; value &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        is_positive(value)             &lt;span style=&#34;color:#6272a4&#34;&gt;# must not raise&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;else&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;with&lt;/span&gt; pytest&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;raises(InvariantViolationError):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            is_positive(value)         &lt;span style=&#34;color:#6272a4&#34;&gt;# must raise&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;→ &lt;code&gt;hypothesis&lt;/code&gt; generates integers (typically 100 by default), checks the property on each. &lt;em&gt;Unexpected&lt;/em&gt; exception → fail. The shrinker finds the smallest failing case (&lt;code&gt;0&lt;/code&gt;? &lt;code&gt;-1&lt;/code&gt;? &lt;code&gt;MIN_INT&lt;/code&gt;?).&lt;/p&gt;</description>
    </item>
    <item>
      <title>05.06 — Husky &#43; lint-staged &#43; commitlint &#43; commitizen</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/05-igoristan/06-husky-commitlint/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/05-igoristan/06-husky-commitlint/</guid>
      <description>&lt;h1 id=&#34;0506husky--lint-staged--commitlint--commitizen&#34;&gt;05.06 — Husky + lint-staged + commitlint + commitizen&lt;a class=&#34;anchor&#34; href=&#34;#0506husky--lint-staged--commitlint--commitizen&#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;Strict commit discipline. Conventional, signed, format-checked.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;tools&#34;&gt;Tools&lt;a class=&#34;anchor&#34; href=&#34;#tools&#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;Tool&lt;/th&gt;&#xA;          &lt;th&gt;Role&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;Husky&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Installs local Git hooks (&lt;code&gt;pre-commit&lt;/code&gt;, &lt;code&gt;commit-msg&lt;/code&gt;)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;lint-staged&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Runs tools &lt;em&gt;only&lt;/em&gt; on &lt;em&gt;staged&lt;/em&gt; files&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;commitlint&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Verifies the commit message follows the convention&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;commitizen + cz-commitlint&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Interactive CLI to &lt;em&gt;compose&lt;/em&gt; a compliant message&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;config&#34;&gt;Config&lt;a class=&#34;anchor&#34; href=&#34;#config&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;husky&#34;&gt;&lt;code&gt;.husky/&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#husky&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;Folder containing the hook scripts:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;.husky/&#xA;├── pre-commit&#xA;└── commit-msg&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;pre-commit&lt;/code&gt;:&lt;/p&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-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;#!/usr/bin/env sh&#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;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;$(&lt;/span&gt;dirname -- &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;$0&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;/_/husky.sh&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pnpm &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;exec&lt;/span&gt; lint-staged&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;commit-msg&lt;/code&gt;:&lt;/p&gt;</description>
    </item>
    <item>
      <title>06.06 — lib/redis.ts &#43; Upstash</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/06-tests-workers/06-redis-upstash/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/06-tests-workers/06-redis-upstash/</guid>
      <description>&lt;h1 id=&#34;0606libredists--upstash&#34;&gt;06.06 — &lt;code&gt;lib/redis.ts&lt;/code&gt; + Upstash&lt;a class=&#34;anchor&#34; href=&#34;#0606libredists--upstash&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/tests-workers/blob/main/lib/redis.ts&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;lib/redis.ts&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; { Redis } &lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;@upstash/redis&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:#ff79c6&#34;&gt;const&lt;/span&gt; redis &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Redis({&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  url: &lt;span style=&#34;color:#8be9fd&#34;&gt;process.env.UPSTASH_REDIS_REST_URL&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  token: &lt;span style=&#34;color:#8be9fd&#34;&gt;process.env.UPSTASH_REDIS_REST_TOKEN&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;&#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;export&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;default&lt;/span&gt; redis;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;why-upstashredis-rather-than-ioredis&#34;&gt;Why &lt;code&gt;@upstash/redis&lt;/code&gt; rather than &lt;code&gt;ioredis&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#why-upstashredis-rather-than-ioredis&#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;Criterion&lt;/th&gt;&#xA;          &lt;th&gt;&lt;code&gt;ioredis&lt;/code&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;code&gt;@upstash/redis&lt;/code&gt;&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Protocol&lt;/td&gt;&#xA;          &lt;td&gt;TCP / RESP&lt;/td&gt;&#xA;          &lt;td&gt;HTTP / REST&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Edge runtime&lt;/td&gt;&#xA;          &lt;td&gt;❌ No&lt;/td&gt;&#xA;          &lt;td&gt;✅ Yes&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Connection&lt;/td&gt;&#xA;          &lt;td&gt;Persistent&lt;/td&gt;&#xA;          &lt;td&gt;Per-request&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Latency (&lt;em&gt;warm&lt;/em&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;&amp;lt; 1ms&lt;/td&gt;&#xA;          &lt;td&gt;50–100ms&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Latency (&lt;em&gt;cold&lt;/em&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;200ms+&lt;/td&gt;&#xA;          &lt;td&gt;50–100ms&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Cost&lt;/td&gt;&#xA;          &lt;td&gt;Variable&lt;/td&gt;&#xA;          &lt;td&gt;Pay-per-request&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Setup&lt;/td&gt;&#xA;          &lt;td&gt;Self-hosted Redis&lt;/td&gt;&#xA;          &lt;td&gt;Upstash dashboard (managed)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Edge doesn&amp;rsquo;t accept persistent TCP connections — each worker is ephemeral. Upstash REST it is.&lt;/p&gt;</description>
    </item>
    <item>
      <title>07.06 — Sacred upload scenarios</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/06-scenarios-sacred-upload/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/06-scenarios-sacred-upload/</guid>
      <description>&lt;h1 id=&#34;0706sacred-upload-scenarios&#34;&gt;07.06 — Sacred upload scenarios&lt;a class=&#34;anchor&#34; href=&#34;#0706sacred-upload-scenarios&#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 &lt;code&gt;/igoristan/sacred-upload&lt;/code&gt; page lets you drag-and-drop a file. Exercises the &lt;strong&gt;file-upload&lt;/strong&gt; pattern on the Ocarina side.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;campaign&#34;&gt;Campaign&lt;a class=&#34;anchor&#34; href=&#34;#campaign&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/tests/campaigns/sacred_upload.py&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;create_igoristan_sacred_upload_campaign&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, drivers_pool) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; TestCampaign:&#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;return&lt;/span&gt; TestCampaign(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Sacred upload&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        suites&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;[&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            create_sacred_upload_happy_paths_test_suite(drivers_pool&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;drivers_pool),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            create_sacred_upload_unhappy_paths_test_suite(drivers_pool&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;drivers_pool),&#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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;scenario-upload-files&#34;&gt;Scenario: upload files&lt;a class=&#34;anchor&#34; href=&#34;#scenario-upload-files&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# tests/scenarios/sacred_upload/upload_files.py&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;scenario_upload_files&lt;/span&gt;(driver, logger):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; SacredUploadPage(driver&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;driver)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fixture_dir &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Path(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;__file__&lt;/span&gt;)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;parent&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;parent&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;parent&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;parent &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;pages&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;sacred_upload&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;fixtures&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    file_to_upload &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; fixture_dir &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;sample.png&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:#ff79c6&#34;&gt;return&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        drive_page(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(page, open_sacred_upload_page)&lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(page, verify_sacred_upload_page)&lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(page, upload_file(file_to_upload))&lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            act(page, verify_file_uploaded(file_to_upload&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;name))&lt;span style=&#34;color:#ff79c6&#34;&gt;...&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;    ]&#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;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test_upload_files &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; create_selenium_test(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Sacred upload - upload a file&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    test_scenario&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt; driver, logger: Scenario(test_chain&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;scenario_upload_files(driver, logger)),&#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;h2 id=&#34;input-typefile&#34;&gt;&lt;code&gt;&amp;lt;input type=&amp;quot;file&amp;quot;&amp;gt;&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#input-typefile&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;SacredUploadPage&lt;/span&gt;(SeleniumTitleMixin, POMBase):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    _file_input &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (By&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;CSS_SELECTOR, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;input[type=&amp;#39;file&amp;#39;]&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    _uploaded_preview &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (By&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;CSS_SELECTOR, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;.upload-preview&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:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;upload_file&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, file_path: Path) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; SacredUploadPage:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        file_input &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_driver&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;find_element(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_file_input)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        file_input&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;send_keys(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;(file_path&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;resolve()))     &lt;span style=&#34;color:#6272a4&#34;&gt;# ← Selenium pattern: send_keys on input[type=file]&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;return&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;verify_file_uploaded&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, filename: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; SacredUploadPage:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        WebDriverWait(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_driver, get_timeout())&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;until(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ec&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;text_to_be_present_in_element(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_uploaded_preview, filename)&#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;return&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Standard Selenium pattern: &lt;code&gt;send_keys(&amp;lt;absolute path&amp;gt;)&lt;/code&gt; on an &lt;code&gt;input[type=file]&lt;/code&gt;, even if the input is hidden. Selenium bypasses browser security for file inputs only.&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>09.03.06 — Author skills</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/06-author/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/06-author/</guid>
      <description>&lt;h1 id=&#34;090306author-skills&#34;&gt;09.03.06 — Author skills&lt;a class=&#34;anchor&#34; href=&#34;#090306author-skills&#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;Skills that &lt;strong&gt;produce a deliverable&lt;/strong&gt;. Tests, probes, docs, reports.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;listing-potentially-non-exhaustive&#34;&gt;Listing (potentially non-exhaustive)&lt;a class=&#34;anchor&#34; href=&#34;#listing-potentially-non-exhaustive&#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;Skill&lt;/th&gt;&#xA;          &lt;th&gt;Target&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;empiricism&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Verify before encoding; don&amp;rsquo;t crush an intentionally failing gap test&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;write-a-probe&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Throwaway &amp;ldquo;probe&amp;rdquo; script, gitignored&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;write-test-strategy&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Generates the test strategy document from the suite&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;extend-coverage&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Extends coverage from existing heritage&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;update-frd-and-tests&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Propagates a spec update&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;manual-reproduction-guide&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Writes a step-by-step reproduction scenario executable by a human&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;manage-backlog&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;BACKLOG.md&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;pr-report&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PR report adapted to context&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;plan-test-effort&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;First-pass test-effort plan: graded requirements, lightweight risk register, relative effort weights (S / M / L), open questions&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;empiricism&#34;&gt;&lt;code&gt;empiricism&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#empiricism&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;input  : user intent (&amp;#34;write a test that asserts X&amp;#34;)&#xA;output : before encoding X:&#xA;            1. empirically verify that X is true&#xA;                - probe&#xA;                - gh api on the source code&#xA;                - curl -v to inspect HTTP responses&#xA;                - ...&#xA;            2. if X confirmed → encode&#xA;            3. if X false → correct intent&#xA;            4. if X ambiguous → ask the human&lt;/code&gt;&lt;/pre&gt;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;&amp;ldquo;&lt;em&gt;Fair point, I&amp;rsquo;m assuming. Let me verify empirically.&lt;/em&gt;&amp;rdquo;&lt;/p&gt;</description>
    </item>
    <item>
      <title>09.06 — Public resources</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/06-public-resources/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/06-public-resources/</guid>
      <description>&lt;h1 id=&#34;0906public-resources&#34;&gt;09.06 — Public resources&lt;a class=&#34;anchor&#34; href=&#34;#0906public-resources&#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;Full table of public URLs exposed by the Holy Book. For humans, LLMs, scrapers.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;listing-potentially-non-exhaustive&#34;&gt;Listing (potentially non-exhaustive)&lt;a class=&#34;anchor&#34; href=&#34;#listing-potentially-non-exhaustive&#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;URL&lt;/th&gt;&#xA;          &lt;th&gt;Content&lt;/th&gt;&#xA;          &lt;th&gt;Target 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;https://mojo-molotov.github.io/ocarina-holy-book/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Home (EN)&lt;/td&gt;&#xA;          &lt;td&gt;Human&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;https://mojo-molotov.github.io/ocarina-holy-book/fr/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Home (FR)&lt;/td&gt;&#xA;          &lt;td&gt;Human&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;https://mojo-molotov.github.io/ocarina-holy-book/ru/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Home (RU)&lt;/td&gt;&#xA;          &lt;td&gt;Human&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;https://mojo-molotov.github.io/ocarina-holy-book/what-is-it&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Article (EN)&lt;/td&gt;&#xA;          &lt;td&gt;Human&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;https://mojo-molotov.github.io/ocarina-holy-book/fr/what-is-it&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Article (FR)&lt;/td&gt;&#xA;          &lt;td&gt;Human&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;https://mojo-molotov.github.io/ocarina-holy-book/ru/what-is-it&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Article (RU)&lt;/td&gt;&#xA;          &lt;td&gt;Human&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&amp;hellip; (other doc pages in EN+FR+RU)&lt;/td&gt;&#xA;          &lt;td&gt;&amp;hellip;&lt;/td&gt;&#xA;          &lt;td&gt;Human&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;https://mojo-molotov.github.io/ocarina-holy-book/llms.txt&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Minimal index for LLMs&lt;/td&gt;&#xA;          &lt;td&gt;LLM&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;https://mojo-molotov.github.io/ocarina-holy-book/llms-full.txt&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;All content in plaintext&lt;/td&gt;&#xA;          &lt;td&gt;LLM, scraper&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;https://mojo-molotov.github.io/ocarina-holy-book/CLAUDE.md&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Full project rules&lt;/td&gt;&#xA;          &lt;td&gt;LLM, human (onboarding)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;https://mojo-molotov.github.io/ocarina-holy-book/CLAUDE.slim.md&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Short project rules&lt;/td&gt;&#xA;          &lt;td&gt;LLM (context budget)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;https://mojo-molotov.github.io/ocarina-holy-book/ocarina-en.pdf&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;A4 PDF docs (EN)&lt;/td&gt;&#xA;          &lt;td&gt;Human (offline)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;https://mojo-molotov.github.io/ocarina-holy-book/ocarina-fr.pdf&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;A4 PDF docs (FR)&lt;/td&gt;&#xA;          &lt;td&gt;Human (offline)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;https://mojo-molotov.github.io/ocarina-holy-book/ocarina-ru.pdf&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;A4 PDF docs (RU)&lt;/td&gt;&#xA;          &lt;td&gt;Human (offline)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;https://mojo-molotov.github.io/ocarina-holy-book/skills/&amp;lt;name&amp;gt;&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;AI skill page&lt;/td&gt;&#xA;          &lt;td&gt;Human, LLM&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;https://mojo-molotov.github.io/ocarina-holy-book/assets/content/...&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Images, audio (article covers, illustrations)&lt;/td&gt;&#xA;          &lt;td&gt;Human (inline rendering)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;llmstxt-emerging-standard&#34;&gt;&lt;code&gt;llms.txt&lt;/code&gt; (emerging standard)&lt;a class=&#34;anchor&#34; href=&#34;#llmstxt-emerging-standard&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Spec: &lt;a href=&#34;https://llmstxt.org/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://llmstxt.org/&lt;/a&gt; (proposed in 2024).&lt;/p&gt;</description>
    </item>
    <item>
      <title>10.06 — ocarina-holy-book workflow</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/06-holy-book-workflow/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/06-holy-book-workflow/</guid>
      <description>&lt;h1 id=&#34;1006ocarina-holy-book-workflow&#34;&gt;10.06 — &lt;code&gt;ocarina-holy-book&lt;/code&gt; workflow&lt;a class=&#34;anchor&#34; href=&#34;#1006ocarina-holy-book-workflow&#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;A single workflow: &lt;code&gt;deploy.yml&lt;/code&gt;. On push main → VitePress build → upload Pages.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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;: Deploy Ocarina Holy Book&#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;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;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;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;TZ&lt;/span&gt;: Europe/Paris&#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;permissions&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;contents&lt;/span&gt;: read&#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;pages&lt;/span&gt;: write&#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;id-token&lt;/span&gt;: write&#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;concurrency&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;group&lt;/span&gt;: pages&#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;cancel-in-progress&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;&#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;build&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;    &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@v6&#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;fetch-depth&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#6272a4&#34;&gt;# ← full clone&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;uses&lt;/span&gt;: pnpm/action-setup@fcf821c621167805dd63a29662bd7cb5676c81a8&#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 pnpm&#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;version&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;11&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;: Use Node.js&#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-node@v6&#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;node-version&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;24&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;cache&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;pnpm&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:#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;run&lt;/span&gt;: pnpm install --frozen-lockfile&#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;: Build&#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;: pnpm build&#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;uses&lt;/span&gt;: actions/upload-pages-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;path&lt;/span&gt;: docs/.vitepress/dist&#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;deploy&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;needs&lt;/span&gt;: build&#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;environment&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;: github-pages&#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;url&lt;/span&gt;: ${{ steps.deployment.outputs.page_url }}&#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;id&lt;/span&gt;: deployment&#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/deploy-pages@v5&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;1-tz-europeparis&#34;&gt;1. &lt;code&gt;TZ: Europe/Paris&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#1-tz-europeparis&#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;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;TZ&lt;/span&gt;: Europe/Paris&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Forces the runner timezone to Europe/Paris. Dates rendered by VitePress (&amp;quot;&lt;em&gt;Last update: 05/18/2026 11h42&lt;/em&gt;&amp;quot;) are in Paris time, not UTC. Leaving it in UTC would cause mysterious date bugs.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.06 — Yung Innanet (kayos) &#43; VX Underground</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/06-yung-innanet-vxug/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/06-yung-innanet-vxug/</guid>
      <description>&lt;h1 id=&#34;1206yung-innanet-kayos--vx-underground&#34;&gt;12.06 — Yung Innanet (&lt;code&gt;kayos&lt;/code&gt;) + VX Underground&lt;a class=&#34;anchor&#34; href=&#34;#1206yung-innanet-kayos--vx-underground&#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 Holy Book&amp;rsquo;s most violent passage borrows lyrics from &lt;strong&gt;&lt;code&gt;true colors&lt;/code&gt;&lt;/strong&gt;, a track by &lt;strong&gt;Yung Innanet&lt;/strong&gt; (alias &lt;strong&gt;kayos&lt;/strong&gt;), original sysadmin of &lt;strong&gt;VX Underground&lt;/strong&gt;. Yung Innanet died on &lt;strong&gt;October 18, 2025&lt;/strong&gt; by firearm suicide.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;1-identity&#34;&gt;1. Identity&lt;a class=&#34;anchor&#34; href=&#34;#1-identity&#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;Field&lt;/th&gt;&#xA;          &lt;th&gt;Value&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Music alias&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Yung Innanet&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Tech handle&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;kayos&lt;/strong&gt; (pronounced /kɛjəʊs/)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Twitter&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;https://x.com/yunginnanet&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;@yunginnanet&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;GitHub&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;https://github.com/yunginnanet/yunginnanet&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;yunginnanet/yunginnanet&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Recognized role&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Original sysadmin of VX Underground&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Death&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;October 18, 2025&lt;/strong&gt;, firearm suicide&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Last.fm profile&lt;/td&gt;&#xA;          &lt;td&gt;&lt;a href=&#34;https://www.last.fm/music/Yung&amp;#43;Innanet/&amp;#43;wiki&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Yung Innanet — Last.fm&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;SoundCloud&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;vxug.fakedoma.in&lt;/em&gt; — &lt;a href=&#34;https://soundcloud.com/queed-inc/vxugfakedomain-vx-underground&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;SoundCloud&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;The news of his death wasn&amp;rsquo;t mainstream-media headlines, consistent with Yung Innanet&amp;rsquo;s &lt;em&gt;underground&lt;/em&gt; profile. It is, however, &lt;strong&gt;public within his scene&lt;/strong&gt;: confirmed notably by tweets from &lt;strong&gt;YTCracker&lt;/strong&gt; and Yung Innanet&amp;rsquo;s &lt;strong&gt;widow&lt;/strong&gt;. There&amp;rsquo;s no point dwelling further — this mention exists only to explain a Holy Book quote that wouldn&amp;rsquo;t make sense without it.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.03.07 — match_page / when</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/03-railway/07-match-page-when/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/03-railway/07-match-page-when/</guid>
      <description>&lt;h1 id=&#34;020307match_page--when&#34;&gt;02.03.07 — &lt;code&gt;match_page&lt;/code&gt; / &lt;code&gt;when&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#020307match_page--when&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing_with_railway/match_page.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing_with_railway/match_page.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Bolted onto the framework &lt;strong&gt;after the fact&lt;/strong&gt;. The Holy Book puts it bluntly: &amp;ldquo;&lt;em&gt;&lt;code&gt;match_page&lt;/code&gt; and &lt;code&gt;when&lt;/code&gt; were added after the fact; the Igoristan was so random that the use case forced itself onto us. Their implementation was simple, proof of the grammar&amp;rsquo;s flexibility: other analogous structures could follow just as easily.&lt;/em&gt;&amp;rdquo;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;a class=&#34;anchor&#34; href=&#34;#the-problem&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Some pages render differently:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Cookies banner showing or not.&lt;/li&gt;&#xA;&lt;li&gt;A/B test serving two UI variants.&lt;/li&gt;&#xA;&lt;li&gt;Degraded mode (maintenance page) instead of the real page.&lt;/li&gt;&#xA;&lt;li&gt;Anti-bot captcha.&lt;/li&gt;&#xA;&lt;li&gt;…&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The scenario needs to &lt;strong&gt;branch&lt;/strong&gt; without breaking the ROP chain.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.05.07 — TestCycle[Driver] &#43; modes</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/05-orchestration/07-test-cycle-modes/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/05-orchestration/07-test-cycle-modes/</guid>
      <description>&lt;h1 id=&#34;020507testcycledriver--modes&#34;&gt;02.05.07 — &lt;code&gt;TestCycle[Driver]&lt;/code&gt; + modes&lt;a class=&#34;anchor&#34; href=&#34;#020507testcycledriver--modes&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/oc_test_cycle.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing/oc_test_cycle.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;One job&lt;/strong&gt;: orchestrate &lt;em&gt;smoke&lt;/em&gt; then &lt;em&gt;main&lt;/em&gt; campaigns, and apply a smoke-failure-handling mode.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;two-modes&#34;&gt;Two modes&lt;a class=&#34;anchor&#34; href=&#34;#two-modes&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Mode &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Literal[&#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;fail-fast-on-first-smoke-campaigns-sequence-fail&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;wait-for-all-smoke-tests&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;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Mode&lt;/th&gt;&#xA;          &lt;th&gt;Behavior&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;fail-fast-on-first-smoke-campaigns-sequence-fail&lt;/code&gt; (default)&lt;/td&gt;&#xA;          &lt;td&gt;First smoke campaign that fails → &lt;strong&gt;following&lt;/strong&gt; ones get &lt;em&gt;skipped&lt;/em&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;wait-for-all-smoke-tests&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Every&lt;/strong&gt; smoke campaign runs. If &lt;em&gt;any&lt;/em&gt; fails, the &lt;em&gt;main&lt;/em&gt; campaigns get skipped.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Case&lt;/th&gt;&#xA;          &lt;th&gt;Mode&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Dependency-style smoke (e.g. &amp;ldquo;login&amp;rdquo; then &amp;ldquo;dashboard accessible&amp;rdquo;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;fail-fast&lt;/code&gt; (login dies → dashboard pointless)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Parallel smoke (e.g. &amp;ldquo;homepage&amp;rdquo; + &amp;ldquo;API ping&amp;rdquo;)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;wait-for-all&lt;/code&gt; (we want to see both)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&lt;code&gt;fail-fast&lt;/code&gt; is the default — it&amp;rsquo;s the common case.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.07 — Watcher[Driver]</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/07-watcher/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/07-watcher/</guid>
      <description>&lt;h1 id=&#34;0207watcherdriver&#34;&gt;02.07 — &lt;code&gt;Watcher[Driver]&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#0207watcherdriver&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/watcher.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing/watcher.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Parallelized observer running as a &lt;em&gt;daemon thread&lt;/em&gt; alongside the &lt;code&gt;test_chain&lt;/code&gt;. Built to catch &lt;strong&gt;unpredictable frictions with no direct impact on the scenario&lt;/strong&gt; that pop up &lt;em&gt;while&lt;/em&gt; the scenario runs: random error toasts, parasitic validations, unexpected popups.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;a class=&#34;anchor&#34; href=&#34;#the-problem&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Holy Book quote (&lt;em&gt;First real-world hurdles&lt;/em&gt;):&lt;/p&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Even more surprisingly: apps displaying error toasts for no apparent reason, or forms flagging validation errors on inputs that are actually correct, without blocking the journey.&lt;/p&gt;</description>
    </item>
    <item>
      <title>03.07 — Discriminated unions &#43; TypeGuard &#43; @final = sealed unions</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/07-discriminated-unions-typeguards/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/07-discriminated-unions-typeguards/</guid>
      <description>&lt;h1 id=&#34;0307discriminated-unions--typeguard--final--sealed-unions&#34;&gt;03.07 — Discriminated unions + TypeGuard + @final = &amp;ldquo;&lt;em&gt;sealed&lt;/em&gt;&amp;rdquo; unions&lt;a class=&#34;anchor&#34; href=&#34;#0307discriminated-unions--typeguard--final--sealed-unions&#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 combo that makes &lt;code&gt;Result[T]&lt;/code&gt; and &lt;code&gt;TestResult&lt;/code&gt; both &lt;strong&gt;typed&lt;/strong&gt; and &lt;strong&gt;usable without casts&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;discriminated-union&#34;&gt;Discriminated union&lt;a class=&#34;anchor&#34; href=&#34;#discriminated-union&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@final&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@dataclass(frozen&lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;Ok&lt;/span&gt;[T](_BaseResult):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    value: T&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error: &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;@final&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@dataclass(frozen&lt;span style=&#34;color:#ff79c6&#34;&gt;=&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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;Fail&lt;/span&gt;(_BaseResult):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error: Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; field(default_factory&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt;: Exception(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Unknown error&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:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Result[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Ok[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; Fail&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;Two constructors: &lt;code&gt;Ok[T]&lt;/code&gt; and &lt;code&gt;Fail&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;Discriminant&lt;/em&gt; is the &lt;em&gt;isinstance check&lt;/em&gt; (not a field like &lt;code&gt;tag: Literal[&amp;quot;ok&amp;quot;, &amp;quot;fail&amp;quot;]&lt;/code&gt;).&lt;/li&gt;&#xA;&lt;li&gt;Both are &lt;code&gt;@final&lt;/code&gt; — &lt;em&gt;no&lt;/em&gt; possible subtype. The union is &lt;strong&gt;exhaustive&lt;/strong&gt;, i.e. &amp;ldquo;&lt;em&gt;sealed&lt;/em&gt;&amp;rdquo;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;narrowing-typeguard&#34;&gt;Narrowing, &lt;code&gt;TypeGuard&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#narrowing-typeguard&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;is_ok&lt;/span&gt;[T](result: Result[T]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; TypeGuard[Ok[T]]:&#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;return&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;isinstance&lt;/span&gt;(result, Ok)&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;is_fail&lt;/span&gt;[T](result: Result[T]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; TypeGuard[Fail]:&#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;return&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;isinstance&lt;/span&gt;(result, Fail)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;handle_result&lt;/span&gt;(result: Result[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&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;if&lt;/span&gt; is_ok(result):&#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;return&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Got: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;result&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;value&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;          &lt;span style=&#34;color:#6272a4&#34;&gt;# mypy: result is Ok[int], so .value exists&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;if&lt;/span&gt; is_fail(result):&#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;return&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Error: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;result&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;error&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;        &lt;span style=&#34;color:#6272a4&#34;&gt;# mypy: result is Fail, so .error exists&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;# mypy can detect this branch is unreachable (exhaustiveness)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;_baseresult&#34;&gt;&lt;code&gt;_BaseResult&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#_baseresult&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_BaseResult&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error: Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This base lets you access &lt;code&gt;result.error&lt;/code&gt; &lt;strong&gt;without narrowing&lt;/strong&gt;:&lt;/p&gt;</description>
    </item>
    <item>
      <title>04.07 — Coverage policy: what&#39;s tested, what isn&#39;t, why</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/04-internal-tests/07-coverage-policy/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/04-internal-tests/07-coverage-policy/</guid>
      <description>&lt;h1 id=&#34;0407coverage-policy-whats-tested-what-isnt-why&#34;&gt;04.07 — Coverage policy: what&amp;rsquo;s tested, what isn&amp;rsquo;t, why&lt;a class=&#34;anchor&#34; href=&#34;#0407coverage-policy-whats-tested-what-isnt-why&#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;Line coverage is &lt;strong&gt;explicitly scoped&lt;/strong&gt; to the pure DSL and agnostic infra. The rest gets tested by other means (cram, types, snapshots, e2e) or stays out of scope (inert shapes).&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;pyprojecttomltoolcoveragerun&#34;&gt;&lt;code&gt;pyproject.toml#tool.coverage.run&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#pyprojecttomltoolcoveragerun&#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-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[tool.coverage.run]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;omit = [&#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;*/__init__.py&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;*/tests/*&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;*/test_*.py&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;src/ocarina/custom_errors/*&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;src/ocarina/custom_types/*&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;src/ocarina/ports/*&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;src/ocarina/opinionated/loggers/custom_types/*&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;src/**/*singleton.py&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;src/**/consts/**&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:#6272a4&#34;&gt;# Selenium adapter layer — exercised only against a real browser.&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;src/ocarina/infra/selenium/*&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;src/ocarina/dsl/testing/selenium/*&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;src/ocarina/opinionated/cli/selenium/*&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;src/ocarina/pom/selenium/muted.py&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:#6272a4&#34;&gt;# Playwright adapter layer — exercised only against a real browser.&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;src/ocarina/infra/playwright/*&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;src/ocarina/dsl/testing/playwright/*&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;src/ocarina/opinionated/cli/playwright/*&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;src/ocarina/pom/playwright/*&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;src/ocarina/opinionated/cli/phantoms.py&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:#6272a4&#34;&gt;# Opinionated loggers — only file_logger.py stays in scope.&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;src/ocarina/opinionated/loggers/create_matching_logger.py&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;src/ocarina/opinionated/loggers/muted_logger.py&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;src/ocarina/opinionated/loggers/print_logger.py&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;src/ocarina/opinionated/loggers/print_and_file_logger.py&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;src/ocarina/opinionated/loggers/utils/*&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:#6272a4&#34;&gt;# Traversed by cram tests&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;src/ocarina/opinionated/cli/store.py&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;src/ocarina/opinionated/cli/builder.py&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;h2 id=&#34;categories&#34;&gt;Categories&lt;a class=&#34;anchor&#34; href=&#34;#categories&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;1-obvious-exclusions&#34;&gt;1. Obvious exclusions&lt;a class=&#34;anchor&#34; href=&#34;#1-obvious-exclusions&#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;Pattern&lt;/th&gt;&#xA;          &lt;th&gt;Justification&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;*/__init__.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Empty modules (Python 3 often doesn&amp;rsquo;t need them)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;*/tests/*&lt;/code&gt;, &lt;code&gt;*/test_*.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Tests don&amp;rsquo;t test themselves&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h3 id=&#34;2-types--errors--ports--shapes&#34;&gt;2. Types / errors / ports / shapes&lt;a class=&#34;anchor&#34; href=&#34;#2-types--errors--ports--shapes&#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;Path&lt;/th&gt;&#xA;          &lt;th&gt;Justification&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;src/ocarina/custom_types/*&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Just &lt;code&gt;type X = ...&lt;/code&gt; and frozen dataclasses. No runtime logic.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/ocarina/custom_errors/*&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Just Exception subclasses. No logic.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/ocarina/ports/*&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;ABCs and Protocols. No behavior.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/ocarina/opinionated/loggers/custom_types/*&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;id.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/**/consts/**&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Constants (&lt;code&gt;LOGGERS_CHOICES = (...)&lt;/code&gt;).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/**/*singleton.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Trivial Singleton wrappers.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h3 id=&#34;3-selenium-and-playwright-layers-e2e-only&#34;&gt;3. Selenium and Playwright layers (e2e only)&lt;a class=&#34;anchor&#34; href=&#34;#3-selenium-and-playwright-layers-e2e-only&#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;Path&lt;/th&gt;&#xA;          &lt;th&gt;Justification&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;src/ocarina/infra/selenium/*&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Real Selenium code (&lt;code&gt;Chrome()&lt;/code&gt;, &lt;code&gt;Firefox()&lt;/code&gt;). Only runs with a real browser.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/ocarina/dsl/testing/selenium/*&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;create_selenium_test&lt;/code&gt;, &lt;code&gt;create_selenium_watcher&lt;/code&gt; (trivial factories on &lt;code&gt;Test&lt;/code&gt; / &lt;code&gt;Watcher&lt;/code&gt;).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/ocarina/opinionated/cli/selenium/*&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;create_selenium_*_cli_store&lt;/code&gt; (reads &lt;code&gt;platform.system&lt;/code&gt;, instantiates).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/ocarina/pom/selenium/muted.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Utility &lt;code&gt;MutedPOM&lt;/code&gt; (nearly empty).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/ocarina/infra/playwright/*&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Real Playwright code + the &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/10-infra/06-playwright-actor/&#34;&gt;&lt;code&gt;PlaywrightDriver&lt;/code&gt; actor&lt;/a&gt;. The end-to-end only proves out against a real browser.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/ocarina/dsl/testing/playwright/*&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;create_playwright_test&lt;/code&gt;, &lt;code&gt;create_playwright_watcher&lt;/code&gt; (same trivial factories).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/ocarina/opinionated/cli/playwright/*&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;create_playwright_cli_store&lt;/code&gt; (Playwright CLI).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;src/ocarina/pom/playwright/*&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;MutedPlaywrightPOM&lt;/code&gt; + &lt;code&gt;mixins&lt;/code&gt; (Null Object, nearly empty).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;These files are &lt;strong&gt;covered&lt;/strong&gt; by:&lt;/p&gt;</description>
    </item>
    <item>
      <title>05.07 — Igoristan CI/CD</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/05-igoristan/07-ci-deploy/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/05-igoristan/07-ci-deploy/</guid>
      <description>&lt;h1 id=&#34;0507igoristan-cicd&#34;&gt;05.07 — Igoristan CI/CD&lt;a class=&#34;anchor&#34; href=&#34;#0507igoristan-cicd&#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: &lt;code&gt;ci-pr.yml&lt;/code&gt; on PR (3 parallel jobs), &lt;code&gt;deploy.yml&lt;/code&gt; on push main (GitHub Pages).&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;ci-pryml&#34;&gt;&lt;code&gt;ci-pr.yml&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#ci-pryml&#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;: CI/PR&#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;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;branches&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:#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;format-check&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;    &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;WIREIT_CACHE&lt;/span&gt;: github&#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@v6&#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;: pnpm/action-setup@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;: { &lt;span style=&#34;color:#ff79c6&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;11&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/setup-node@v6&#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;: { &lt;span style=&#34;color:#ff79c6&#34;&gt;node-version: 24, cache&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;pnpm&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;uses&lt;/span&gt;: google/wireit@setup-github-actions-caching/v2&#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 project&#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;: make install&#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;: Formatcheck&#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;: make ci:format-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;lint&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;    &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;WIREIT_CACHE&lt;/span&gt;: github&#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:#6272a4&#34;&gt;# ... same ...&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@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;path&lt;/span&gt;: .eslintcache&#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;: eslint-cache-${{ hashFiles(&amp;#39;eslint.config.ts&amp;#39;, &amp;#39;src/**/*.*&amp;#39;, &amp;#39;package.json&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;: 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;: pnpm lint&#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;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;    &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;WIREIT_CACHE&lt;/span&gt;: github&#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:#6272a4&#34;&gt;# ... same ...&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;: Typecheck&#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;: pnpm typecheck&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;Job&lt;/th&gt;&#xA;          &lt;th&gt;Command&lt;/th&gt;&#xA;          &lt;th&gt;Cache&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;format-check&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;pnpm exec prettier . --check&lt;/code&gt; (via wireit)&lt;/td&gt;&#xA;          &lt;td&gt;wireit + node_modules&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;lint&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;pnpm exec eslint ... --max-warnings 0 --cache&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;wireit + &lt;code&gt;.eslintcache&lt;/code&gt; (actions/cache)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;typecheck&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;tsc --build --pretty tsconfig.json&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;wireit + &lt;code&gt;tsbuildinfo&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;                          PR opened / push&#xA;                                  │&#xA;                ┌─────────────────┼─────────────────┐&#xA;                ▼                 ▼                 ▼&#xA;        ┌──────────────┐ ┌──────────────┐ ┌──────────────┐&#xA;        │ format-check │ │     lint     │ │  typecheck   │&#xA;        │ (1-2 minutes)│ │ (1-2 minutes)│ │ (1-2 minutes)│&#xA;        └──────────────┘ └──────────────┘ └──────────────┘&#xA;                │                 │                 │&#xA;                └─────────────────┴─────────────────┘&#xA;                                  ▼&#xA;                             PR mergeable&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;deployyml&#34;&gt;&lt;code&gt;deploy.yml&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#deployyml&#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;: Deploy to GitHub Pages&#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;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;permissions&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;contents&lt;/span&gt;: read&#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;pages&lt;/span&gt;: write&#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;id-token&lt;/span&gt;: write&#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;concurrency&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;group&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;pages&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;cancel-in-progress&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;&#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;build&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;    &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@v6&#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;: pnpm/action-setup@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;: { &lt;span style=&#34;color:#ff79c6&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;11&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/setup-node@v6&#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;: { &lt;span style=&#34;color:#ff79c6&#34;&gt;node-version: 24, cache&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;pnpm&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;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;run&lt;/span&gt;: pnpm install --frozen-lockfile&#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;: Build&#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;: pnpm build&#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 Pages artifact&#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-pages-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;: { &lt;span style=&#34;color:#ff79c6&#34;&gt;path&lt;/span&gt;: dist/client }&#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;deploy&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;needs&lt;/span&gt;: build&#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;environment&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;: github-pages&#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;url&lt;/span&gt;: ${{ steps.deployment.outputs.page_url }}&#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;id&lt;/span&gt;: deployment&#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/deploy-pages@v5&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;Job&lt;/th&gt;&#xA;          &lt;th&gt;Effect&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;build&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;pnpm install --frozen-lockfile&lt;/code&gt; → &lt;code&gt;pnpm build&lt;/code&gt; (= &lt;code&gt;wireit build&lt;/code&gt; = &lt;code&gt;lint → typecheck → vike build&lt;/code&gt;) → upload &lt;code&gt;dist/client&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;deploy&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;actions/deploy-pages@v5&lt;/code&gt; → publishes the artifact → URL = &lt;a href=&#34;https://mojo-molotov.github.io/igoristan/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://mojo-molotov.github.io/igoristan/&lt;/a&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;concurrency-pages--cancel-in-progress-false&#34;&gt;&lt;code&gt;concurrency: pages&lt;/code&gt; + &lt;code&gt;cancel-in-progress: false&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#concurrency-pages--cancel-in-progress-false&#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;concurrency&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;group&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;pages&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;cancel-in-progress&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Prevents &lt;strong&gt;two simultaneous deployments&lt;/strong&gt; (race condition).&lt;br&gt;&#xA;The second waits (&lt;code&gt;cancel-in-progress: false&lt;/code&gt; → we &lt;em&gt;don&amp;rsquo;t&lt;/em&gt; cancel the running one).&lt;/p&gt;</description>
    </item>
    <item>
      <title>06.07 — OTP coordination flow &#43; deliberate imprecision</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/06-tests-workers/07-otp-coordination-flow/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/06-tests-workers/07-otp-coordination-flow/</guid>
      <description>&lt;h1 id=&#34;0607otp-coordination-flow--deliberate-imprecision&#34;&gt;06.07 — OTP coordination flow + deliberate imprecision&lt;a class=&#34;anchor&#34; href=&#34;#0607otp-coordination-flow--deliberate-imprecision&#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 backend&amp;rsquo;s reason to exist: let N parallel Ocarina workers &lt;strong&gt;retrieve the right OTP&lt;/strong&gt; for their &lt;em&gt;user&lt;/em&gt;, even when several OTPs are generated.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;flow&#34;&gt;Flow&lt;a class=&#34;anchor&#34; href=&#34;#flow&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;   ┌───────────────────────────────────────────────────────────────────┐&#xA;   │               Worker (among --workers 3 of Ocarina)               │&#xA;   └───────────────────────────────────────────────────────────────────┘&#xA;                                     │&#xA;                                     ▼&#xA;   ┌───────────────────────────────────────────────────────────────────┐&#xA;   │ Selenium opens the Dashboard login page                           │&#xA;   └─────────────────────────────────┬─────────────────────────────────┘&#xA;                                     ▼&#xA;   ┌───────────────────────────────────────────────────────────────────┐&#xA;   │ Acquire the distributed Redis lock (OTP_SEND_LOCK_KEY)            │&#xA;   │   → only this worker can click &amp;#34;Send OTP&amp;#34; during ACQ              │&#xA;   └─────────────────────────────────┬─────────────────────────────────┘&#xA;                                     ▼&#xA;   ┌───────────────────────────────────────────────────────────────────┐&#xA;   │ min_utc_date = datetime.now(UTC)                                  │&#xA;   └─────────────────────────────────┬─────────────────────────────────┘&#xA;                                     ▼&#xA;   ┌───────────────────────────────────────────────────────────────────┐&#xA;   │ L1 cache: record min_utc_date + username in the cache             │&#xA;   │   (keys reserved by reserve_free_cache_key)                       │&#xA;   └─────────────────────────────────┬─────────────────────────────────┘&#xA;                                     ▼&#xA;   ┌───────────────────────────────────────────────────────────────────┐&#xA;   │ Selenium types username + password + ticks OTP + click &amp;#34;REQ OTP&amp;#34;  │&#xA;   └─────────────────────────────────┬─────────────────────────────────┘&#xA;                                     ▼&#xA;   ┌───────────────────────────────────────────────────────────────────┐&#xA;   │ IGORISTAN UI: fetch /api/otp?_user=&amp;lt;username&amp;gt;                     │&#xA;   │   (x-api-key typed by Selenium into the UI)                       │&#xA;   └─────────────────────────────────┬─────────────────────────────────┘&#xA;                                     ▼&#xA;   ┌───────────────────────────────────────────────────────────────────┐&#xA;   │ TESTS-WORKERS /api/otp:                                           │&#xA;   │   generate(secret) → otpCode                                      │&#xA;   │   createdAt = floor(now/1000)*1000  ← ms stripped                 │&#xA;   │   event = { _user, otpCode, createdAt, expiresAt, ... }           │&#xA;   │   redis.set(&amp;#34;otp:&amp;lt;now&amp;gt;:&amp;lt;uuid&amp;gt;&amp;#34;, JSON, EX 360)                     │&#xA;   │   return event                                                    │&#xA;   └──────────────────────────────────┬────────────────────────────────┘&#xA;                                      ▼&#xA;   ┌───────────────────────────────────────────────────────────────────┐&#xA;   │ IGORISTAN UI: displays OTP screen                                 │&#xA;   └──────────────────────────────────┬────────────────────────────────┘&#xA;                                      ▼&#xA;   ┌───────────────────────────────────────────────────────────────────┐&#xA;   │ Release the OTP_SEND_LOCK_KEY Redis lock                          │&#xA;   └──────────────────────────────────┬────────────────────────────────┘&#xA;                                      ▼&#xA;   ┌───────────────────────────────────────────────────────────────────┐&#xA;   │ Selenium: retrieve_dashboard_otp_code(min_utc_date, _user)        │&#xA;   │   ↓                                                               │&#xA;   │   GET /api/otp-history  (x-api-key = IGOR_API_KEY)                │&#xA;   │   ↓                                                               │&#xA;   │   TESTS-WORKERS: SCAN otp:* + MGET → all events                   │&#xA;   │   ↓                                                               │&#xA;   │   client-side filter by _user                                     │&#xA;   │   filter createdAt &amp;gt;= min_utc_date - 1s                           │&#xA;   │   sort ASC by createdAt                                           │&#xA;   │   return first.otpCode                                            │&#xA;   └──────────────────────────────────┬────────────────────────────────┘&#xA;                                      ▼&#xA;   ┌───────────────────────────────────────────────────────────────────┐&#xA;   │ Selenium types the OTP into the Igoristan UI                      │&#xA;   │   → AUTHENTICATED_WITH_MFA                                        │&#xA;   └───────────────────────────────────────────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;race-conditions&#34;&gt;Race conditions&lt;a class=&#34;anchor&#34; href=&#34;#race-conditions&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Worker A: &lt;code&gt;min_utc_date_A = 13:27:53.123&lt;/code&gt;, generates OTP_A at 13:27:53.250.&lt;/li&gt;&#xA;&lt;li&gt;Worker B: &lt;code&gt;min_utc_date_B = 13:27:53.130&lt;/code&gt;, generates OTP_B at 13:27:53.470.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Without coordination, A could pick up OTP_B instead of OTP_A — the timestamps are close and truncated to the second.&lt;/p&gt;</description>
    </item>
    <item>
      <title>07.07 — HumanizedDriver</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/07-humanized-driver/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/07-humanized-driver/</guid>
      <description>&lt;h1 id=&#34;0707humanizeddriver&#34;&gt;07.07 — &lt;code&gt;HumanizedDriver&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#0707humanizeddriver&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina-example/blob/main/src/lib/ext/selenium/humanize/proxy.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/lib/ext/selenium/humanize/proxy.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Proxy pattern: wraps a Selenium &lt;code&gt;WebDriver&lt;/code&gt; and intercepts only &lt;code&gt;find_element*&lt;/code&gt; to return &lt;code&gt;WebElement&lt;/code&gt;s whose &lt;code&gt;send_keys&lt;/code&gt; are &lt;em&gt;humanized&lt;/em&gt; (slow typing with typos, hesitations, corrections).&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;HumanizedDriver&lt;/span&gt;(WebDriver):&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, driver: WebDriver, &lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;keyboard_config: Unpack[KeyboardConfig]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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:#8be9fd;font-style:italic&#34;&gt;object&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&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;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_driver &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; driver&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_config &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; keyboard_config&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;find_element&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, by: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; RelativeBy &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;, value: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; _HumanizedWebElement:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        element &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_driver&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;find_element(by, value)&#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;return&lt;/span&gt; _HumanizedWebElement(element, &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_config)&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;find_elements&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, by: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; RelativeBy &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;, value: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;list&lt;/span&gt;[WebElement]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        elements &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_driver&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;find_elements(by, value)&#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;return&lt;/span&gt; [_HumanizedWebElement(el, &lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_config) &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; el &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; elements]&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;__getattr__&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, name: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&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;return&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;getattr&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;_driver, name)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;1-inherits-from-webdriver&#34;&gt;1. Inherits from &lt;code&gt;WebDriver&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#1-inherits-from-webdriver&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;HumanizedDriver&lt;/span&gt;(WebDriver):&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;HumanizedDriver&lt;/code&gt; passes &lt;code&gt;isinstance(x, WebDriver)&lt;/code&gt; checks.&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>09.03.07 — Refactor skills</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/07-refactor/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/07-refactor/</guid>
      <description>&lt;h1 id=&#34;090307refactor-skills&#34;&gt;09.03.07 — Refactor skills&lt;a class=&#34;anchor&#34; href=&#34;#090307refactor-skills&#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;Skills that &lt;strong&gt;refactor&lt;/strong&gt; the existing automated-test base.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;listing-potentially-non-exhaustive&#34;&gt;Listing (potentially non-exhaustive)&lt;a class=&#34;anchor&#34; href=&#34;#listing-potentially-non-exhaustive&#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;Skill&lt;/th&gt;&#xA;          &lt;th&gt;Target&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;refactor-fragmentation&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;DRY based on user preference&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;introduce-pom-retries&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;POM-internal retries, with split (first-try + with-retries)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;refactor-fragmentation&#34;&gt;&lt;code&gt;refactor-fragmentation&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#refactor-fragmentation&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;input  : the test base&#xA;output : DRY refactor suggestions:&#xA;            - blocks repeated in 3+ scenarios → extract into a fragment&#xA;            - near-identical connectors → extract a parameterized connector&#xA;            - repeated log+screenshot sequences → extract a helper&#xA;         + per-case recommendation&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;:&lt;/p&gt;</description>
    </item>
    <item>
      <title>10.07 — tests-workers: no GitHub CI, auto Vercel deploy</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/07-tests-workers-vercel/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/10-cicd/07-tests-workers-vercel/</guid>
      <description>&lt;h1 id=&#34;1007tests-workers-no-github-ci-auto-vercel-deploy&#34;&gt;10.07 — &lt;code&gt;tests-workers&lt;/code&gt;: no GitHub CI, auto Vercel deploy&lt;a class=&#34;anchor&#34; href=&#34;#1007tests-workers-no-github-ci-auto-vercel-deploy&#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 only repo in the ecosystem &lt;strong&gt;without a GitHub Actions workflow&lt;/strong&gt;. Everything goes through Vercel.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;content-of-github&#34;&gt;Content of &lt;code&gt;.github/&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#content-of-github&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;tests-workers/&#xA;└── (no .github/ — no workflows)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;No &lt;code&gt;ci.yml&lt;/code&gt;. No &lt;code&gt;deploy.yml&lt;/code&gt;. No GitHub action.&lt;/p&gt;&#xA;&lt;h2 id=&#34;verceljson&#34;&gt;&lt;code&gt;vercel.json&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#verceljson&#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-json&#34; data-lang=&#34;json&#34;&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;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;2&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;&amp;#34;buildCommand&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;echo &amp;#39;No build required&amp;#39;&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;&amp;#34;outputDirectory&amp;#34;&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:#ff79c6&#34;&gt;&amp;#34;framework&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;null&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;Vercel &lt;em&gt;detects&lt;/em&gt; the project as &lt;em&gt;TypeScript Edge Functions&lt;/em&gt;, builds on the fly (transpiles TS at runtime).&lt;/p&gt;&#xA;&lt;h2 id=&#34;packagejsonscripts&#34;&gt;&lt;code&gt;package.json#scripts&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#packagejsonscripts&#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-json&#34; data-lang=&#34;json&#34;&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;&amp;#34;scripts&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;&amp;#34;vercel-build&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;echo &amp;#39;Build skipped&amp;#39;&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;vercel-build&lt;/code&gt; hook: &lt;code&gt;echo&lt;/code&gt; no-op.&lt;br&gt;&#xA;Vercel doesn&amp;rsquo;t try to build.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.07 — The underlying movement</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/07-underlying-movement/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/07-underlying-movement/</guid>
      <description>&lt;h1 id=&#34;1207the-underlying-movement&#34;&gt;12.07 — The underlying movement&lt;a class=&#34;anchor&#34; href=&#34;#1207the-underlying-movement&#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 possible trajectories for anyone in the 1999-2012 hacker scene: (1) stay &lt;em&gt;black hat&lt;/em&gt; and end up in prison (at best), (2) drop off entirely, (3) &lt;strong&gt;rebuild oneself alone through Research&lt;/strong&gt;. The Holy Book speaks of the third. That&amp;rsquo;s where Ocarina comes from.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;1-the-key-passage-from-the-holy-book&#34;&gt;1. The key passage from the Holy Book&lt;a class=&#34;anchor&#34; href=&#34;#1-the-key-passage-from-the-holy-book&#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;&lt;em&gt;From Hell to where we are today.&lt;/em&gt;&lt;br&gt;&#xA;&lt;em&gt;Saved by Research, by good values, by thankless work. Not the kind that makes you shine. The kind passed down by those who know how to detect a potential and save it before it&amp;rsquo;s too late.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.05.08 — filter_tests_by_ids</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/05-orchestration/08-filter-tests-by-ids/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/05-orchestration/08-filter-tests-by-ids/</guid>
      <description>&lt;h1 id=&#34;020508filter_tests_by_ids&#34;&gt;02.05.08 — &lt;code&gt;filter_tests_by_ids&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#020508filter_tests_by_ids&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/dsl/testing/filter_tests_by_ids.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/dsl/testing/filter_tests_by_ids.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Called by &lt;code&gt;TestSuite.__init__&lt;/code&gt; to apply the CLI flags &lt;code&gt;--only &amp;lt;ids&amp;gt;&lt;/code&gt; and &lt;code&gt;--exclude &amp;lt;ids&amp;gt;&lt;/code&gt;. Mutually exclusive — can&amp;rsquo;t pass both.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;filter_tests_by_ids&lt;/span&gt;[Driver](&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tests: Sequence[Test[Driver]],&#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;*&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    only: Iterable[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    exclude: Iterable[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logger: ILogger,&#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;-&amp;gt;&lt;/span&gt; Sequence[Test[Driver]]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    only_set &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;set&lt;/span&gt;(only)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    exclude_set &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;set&lt;/span&gt;(exclude)&#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;if&lt;/span&gt; only_set &lt;span style=&#34;color:#ff79c6&#34;&gt;and&lt;/span&gt; exclude_set:&#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;raise&lt;/span&gt; ValueError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;--only and --exclude cannot be used together&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;    known_ids &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; {test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;test_id &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; test &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; tests}&#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;if&lt;/span&gt; only_set:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        matched &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; only_set &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;amp;&lt;/span&gt; known_ids&#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; matched:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            joined &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;join(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sorted&lt;/span&gt;(matched))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            logger&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;info(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;--only: matched test IDs: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;joined&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&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;return&lt;/span&gt; [test &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; test &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; tests &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;test_id &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; only_set]&#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;if&lt;/span&gt; exclude_set:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        matched &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; exclude_set &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;amp;&lt;/span&gt; known_ids&#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; matched:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            joined &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;join(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;sorted&lt;/span&gt;(matched))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            logger&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;info(&lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;--exclude: matched test IDs: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;joined&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&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;return&lt;/span&gt; [test &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; test &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; tests &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; test&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;test_id &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; exclude_set]&#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;return&lt;/span&gt; tests&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;four-guarantees&#34;&gt;Four guarantees&lt;a class=&#34;anchor&#34; href=&#34;#four-guarantees&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;1-mutex---only----exclude&#34;&gt;1. Mutex &lt;code&gt;--only&lt;/code&gt; / &lt;code&gt;--exclude&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#1-mutex---only----exclude&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; only_set &lt;span style=&#34;color:#ff79c6&#34;&gt;and&lt;/span&gt; exclude_set:&#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;raise&lt;/span&gt; ValueError(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;--only and --exclude cannot be used together&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Passing both is a &lt;em&gt;runtime&lt;/em&gt; error (not a &lt;em&gt;mypy&lt;/em&gt; one). Semantically nonsense: &amp;ldquo;keep only A, B&amp;rdquo; AND &amp;ldquo;exclude C, D&amp;rdquo;? At the CLI level, doesn&amp;rsquo;t make sense. The mutex kills ambiguity.&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.08 — POMBase</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/08-pom-base/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/08-pom-base/</guid>
      <description>&lt;h1 id=&#34;0208pombase&#34;&gt;02.08 — &lt;code&gt;POMBase&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#0208pombase&#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;Source file: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/blob/main/src/ocarina/pom/base.py&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/pom/base.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Framework-agnostic&lt;/strong&gt; abstract base for the Page Object Model. Two required methods. Zero mention of Selenium.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;POMBase&lt;/span&gt;(ABC):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    @abstractmethod&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;verify&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, timeout: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;float&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Self:&#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;...&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;    @abstractmethod&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;get_current_title&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&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;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Six bytes of contract (the two signatures). That&amp;rsquo;s all.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-two-methods&#34;&gt;Why two methods&lt;a class=&#34;anchor&#34; href=&#34;#why-two-methods&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;verifytimeoutnone---self&#34;&gt;&lt;code&gt;verify(timeout=None) -&amp;gt; Self&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#verifytimeoutnone---self&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;Purpose: &lt;strong&gt;prove we&amp;rsquo;re on the right page&lt;/strong&gt;. Otherwise, raise.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;timeout&lt;/code&gt; lets you wait &lt;em&gt;up to a given time&lt;/em&gt; for the page to load (handy for Selenium &lt;code&gt;WebDriverWait&lt;/code&gt;).&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;None&lt;/code&gt; default lets the implementation decide (typically reading &lt;code&gt;get_timeout()&lt;/code&gt; which reads the CLI).&lt;/li&gt;&#xA;&lt;li&gt;Returning &lt;code&gt;Self&lt;/code&gt; enables fluent &lt;em&gt;method chaining&lt;/em&gt; (&lt;code&gt;MyPage(...).open().verify().click()&lt;/code&gt;).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The only method test-side that&amp;rsquo;s truly needed to guarantee &amp;ldquo;I&amp;rsquo;m on the page I want.&amp;rdquo; Everything else (&lt;code&gt;click_xxx&lt;/code&gt;, &lt;code&gt;enter_xxx&lt;/code&gt;) is on the subclass.&lt;/p&gt;</description>
    </item>
    <item>
      <title>04.08 — Allure &#43; composite action allure-history &#43; GH Pages deploy</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/04-internal-tests/08-allure-history/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/04-internal-tests/08-allure-history/</guid>
      <description>&lt;h1 id=&#34;0408allure--composite-action-allure-history--gh-pages-deploy&#34;&gt;04.08 — Allure + composite action &lt;code&gt;allure-history&lt;/code&gt; + GH Pages deploy&lt;a class=&#34;anchor&#34; href=&#34;#0408allure--composite-action-allure-history--gh-pages-deploy&#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 Allure report gets generated on every CI, &lt;em&gt;archived&lt;/em&gt; on a dedicated Git branch, then &lt;em&gt;published&lt;/em&gt; on GitHub Pages. Live URL: &lt;a href=&#34;https://mojo-molotov.github.io/ocarina/allure-report/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://mojo-molotov.github.io/ocarina/allure-report/&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;pipeline&#34;&gt;Pipeline&lt;a class=&#34;anchor&#34; href=&#34;#pipeline&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;┌────────────────────────────────────────────────────────┐&#xA;│ main_ci.yml (push/PR main, or dispatch)                │&#xA;└──────────────────┬─────────────────────────────────────┘&#xA;                   ▼&#xA;   ┌───────────────────────────────────┐&#xA;   │ job : test                        │&#xA;   │  - matrix (ubuntu / windows × py) │&#xA;   │  - make test (cram + pytest)      │&#xA;   │  - upload-artifact allure-results │&#xA;   └───────────────┬───────────────────┘&#xA;                   ▼&#xA;   ┌───────────────────────────────────┐&#xA;   │ job : allure-history              │&#xA;   │  - download all allure-results    │&#xA;   │  - local composite action:        │&#xA;   │       allure-history/action.yml   │&#xA;   │  - upload-artifact allure-report  │&#xA;   └───────────────┬───────────────────┘&#xA;                   ▼&#xA;   ┌───────────────────────────────────┐&#xA;   │ job : deploy                      │&#xA;   │  - download allure-report         │&#xA;   │  - prepare pages-root/            │&#xA;   │  - actions/upload-pages-artifact  │&#xA;   │  - actions/deploy-pages           │&#xA;   └───────────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;allure-history&#34;&gt;&lt;code&gt;allure-history&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#allure-history&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;.github/actions/allure-history/action.yml&lt;/code&gt;: composite action &lt;strong&gt;local&lt;/strong&gt; to the repo (&lt;code&gt;uses: ./.github/actions/allure-history&lt;/code&gt;).&lt;/p&gt;</description>
    </item>
    <item>
      <title>07.08 — L1 cache &#43; reserved keys &#43; distributed Redis locks</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/08-caches-locks/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/08-caches-locks/</guid>
      <description>&lt;h1 id=&#34;0708l1-cache--reserved-keys--distributed-redis-locks&#34;&gt;07.08 — L1 cache + reserved keys + distributed Redis locks&lt;a class=&#34;anchor&#34; href=&#34;#0708l1-cache--reserved-keys--distributed-redis-locks&#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;Internal coordination mechanics: an in-memory cache to share values &lt;em&gt;within a test&lt;/em&gt;, a Redis lock to serialize a click &lt;em&gt;across several workers&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;l1-cache&#34;&gt;L1 cache&lt;a class=&#34;anchor&#34; href=&#34;#l1-cache&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/caches/l1.py&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;from&lt;/span&gt; dogpile.cache &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; make_region&#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;in_memory_cache_with_30m_ttl &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; make_region()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;configure(&#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;dogpile.cache.memory&amp;#34;&lt;/span&gt;, expiration_time&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;30&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;60&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;One global cache, &lt;code&gt;dogpile.cache.memory&lt;/code&gt; backend, 30-minute TTL. Everything in RAM — no Redis for local-process state.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-an-l1-cache&#34;&gt;Why an L1 cache?&lt;a class=&#34;anchor&#34; href=&#34;#why-an-l1-cache&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Ocarina&amp;rsquo;s DSL is &lt;strong&gt;static&lt;/strong&gt;: a scenario describes a sequence of &lt;code&gt;act&lt;/code&gt;s. But sometimes you need to &lt;strong&gt;share a value&lt;/strong&gt; between two acts:&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>09.03.08 — State skill</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/08-state/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/08-state/</guid>
      <description>&lt;h1 id=&#34;090308state-skill&#34;&gt;09.03.08 — State skill&lt;a class=&#34;anchor&#34; href=&#34;#090308state-skill&#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;A single skill: &lt;code&gt;question-state&lt;/code&gt;. Interrogates the environment before believing a result.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;the-skill&#34;&gt;The skill&lt;a class=&#34;anchor&#34; href=&#34;#the-skill&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;input  : a surprising result (&amp;#34;the test fails&amp;#34;)&#xA;output : a series of questions about state:&#xA;            - which browser are we running on?&#xA;            - which commit?&#xA;            - is the SUT warm or cold?&#xA;            - is Redis available?&#xA;            - what&amp;#39;s the configured wait-timeout?&#xA;            - what&amp;#39;s the driver version?&#xA;            - does ChromeDriver have its password-manager-off fix?&#xA;            - is this an isolated run or a parallel run?&#xA;            - are other tests touching the same SUT resources?&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;approach&#34;&gt;Approach&lt;a class=&#34;anchor&#34; href=&#34;#approach&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;:&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.08 — Ocarina in the testing industry</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/08-ocarina-in-testing-industry/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/08-ocarina-in-testing-industry/</guid>
      <description>&lt;h1 id=&#34;1208ocarina-in-the-testing-industry&#34;&gt;12.08 — Ocarina in the testing industry&lt;a class=&#34;anchor&#34; href=&#34;#1208ocarina-in-the-testing-industry&#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;Ocarina isn&amp;rsquo;t a &lt;em&gt;better&lt;/em&gt; pytest, isn&amp;rsquo;t a &lt;em&gt;better&lt;/em&gt; Robot Framework, isn&amp;rsquo;t a &lt;em&gt;better&lt;/em&gt; Cypress. It&amp;rsquo;s &lt;strong&gt;structurally elsewhere&lt;/strong&gt;. This chapter tries to explain the &lt;em&gt;shift&lt;/em&gt; it represents and the head start it takes.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;1-the-current-industry-map&#34;&gt;1. The current industry map&lt;a class=&#34;anchor&#34; href=&#34;#1-the-current-industry-map&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;three-big-e2e-tool-families&#34;&gt;Three big e2e tool families&lt;a class=&#34;anchor&#34; href=&#34;#three-big-e2e-tool-families&#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;Family&lt;/th&gt;&#xA;          &lt;th&gt;Examples&lt;/th&gt;&#xA;          &lt;th&gt;Model&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;Text DSLs&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Robot Framework, Cucumber + Gherkin&lt;/td&gt;&#xA;          &lt;td&gt;Permanent translation layer between &lt;em&gt;text&lt;/em&gt; and &lt;em&gt;code&lt;/em&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Runner plugins&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;pytest-selenium, pytest-playwright, pytest-bdd&lt;/td&gt;&#xA;          &lt;td&gt;Grafted onto pytest, inherit its lifecycle&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Vendor SaaS&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;BrowserStack, Sauce Labs, LambdaTest, Cypress Dashboard&lt;/td&gt;&#xA;          &lt;td&gt;Local runner + paid execution cloud&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h3 id=&#34;three-shared-friction-surfaces&#34;&gt;Three shared &lt;em&gt;friction surfaces&lt;/em&gt;&lt;a class=&#34;anchor&#34; href=&#34;#three-shared-friction-surfaces&#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;Friction&lt;/th&gt;&#xA;          &lt;th&gt;Manifestation&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;Marketing &amp;gt; Technical&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Tools sold on demos, conferences, and &lt;em&gt;oriented&lt;/em&gt; certifications. &lt;em&gt;Hype&lt;/em&gt; drives adoption.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Vendor lock-in&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Once adopted, refactor is costly, &lt;em&gt;hostage taking&lt;/em&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Misplaced cognitive effort&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Understanding the tool takes longer than doing the work.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;2-ocarinas-technical-bet&#34;&gt;2. Ocarina&amp;rsquo;s technical bet&lt;a class=&#34;anchor&#34; href=&#34;#2-ocarinas-technical-bet&#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;No text DSL&lt;/strong&gt; → pure Python, &lt;em&gt;embedded DSL&lt;/em&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;No pytest plugin&lt;/strong&gt; → integrated runner, absolute ISTQB fidelity.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;No &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;&lt;/strong&gt; → simplicity, Selenium-synchronous compatibility, no &amp;ldquo;&lt;em&gt;geek tricks&lt;/em&gt;&amp;rdquo;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;No SaaS&lt;/strong&gt; → all local, very dense core, &lt;em&gt;white box&lt;/em&gt;.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;See also: &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/11-independence/03-explicit-refusals/&#34;&gt;&lt;code&gt;../11-independence/03-explicit-refusals.md&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.09 — Ports: ILogger, ITakeScreenshot</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/09-ports/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/09-ports/</guid>
      <description>&lt;h1 id=&#34;0209ports-ilogger-itakescreenshot&#34;&gt;02.09 — Ports: &lt;code&gt;ILogger&lt;/code&gt;, &lt;code&gt;ITakeScreenshot&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#0209ports-ilogger-itakescreenshot&#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;Source folder: &lt;a href=&#34;https://github.com/mojo-molotov/ocarina/tree/main/src/ocarina/ports&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;src/ocarina/ports/&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Two ports. That&amp;rsquo;s it. Everything else (&lt;code&gt;WebDriversPool&lt;/code&gt;, &lt;code&gt;Screenshotter&lt;/code&gt;, etc.) lives under &lt;code&gt;infra/&lt;/code&gt;, not &lt;code&gt;ports/&lt;/code&gt;. The line is sharp: a &lt;em&gt;port&lt;/em&gt; is an abstraction the DSL sits on; an &lt;em&gt;infra&lt;/em&gt; is the implementation of &lt;em&gt;adapters&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;ilogger&#34;&gt;&lt;code&gt;ILogger&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#ilogger&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;ILogger&lt;/span&gt;(ABC):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    @abstractmethod&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;set_prefix&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, prefix_thunk: Thunk[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Self: &lt;span style=&#34;color:#ff79c6&#34;&gt;...&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;    @abstractmethod&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;set_domain_taxonomy&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, taxonomy: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;tuple&lt;/span&gt;[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Self: &lt;span style=&#34;color:#ff79c6&#34;&gt;...&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;    @abstractmethod&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;raw&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;args: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;object&lt;/span&gt;, stream: SupportsWrite[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;kwargs: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;object&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;...&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;    @abstractmethod&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;critical&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, msg: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;args, exc: Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;kwargs) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;...&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;    @abstractmethod&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;error&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, msg: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;args, exc: Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;kwargs) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;...&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;    @abstractmethod&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;warning&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, msg: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;args, exc: Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;kwargs) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;...&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;    @abstractmethod&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;info&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, msg: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;args, exc: Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;kwargs) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;...&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;    @abstractmethod&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;debug&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, msg: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;args, exc: Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;kwargs) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;...&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;    @abstractmethod&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;test_name&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, msg: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;args, exc: Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;kwargs) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;...&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;    @abstractmethod&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;success&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, msg: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;args, exc: Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;kwargs) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;...&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;    @abstractmethod&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;exception&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, msg: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;args, exc: Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;**&lt;/span&gt;kwargs) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;...&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;    @abstractmethod&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;cleanup&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;...&lt;/span&gt;&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;Method&lt;/th&gt;&#xA;          &lt;th&gt;Semantics&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;set_prefix(thunk)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Lazy&lt;/strong&gt; prefix applied on every log (for timestamps, threads…). Returns &lt;code&gt;Self&lt;/code&gt; for chaining.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;set_domain_taxonomy(taxonomy)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Sets the domain hierarchy (&lt;code&gt;(cycle, campaign, suite, test)&lt;/code&gt;). Critical for &lt;code&gt;FileLogger&lt;/code&gt;, which builds a file tree.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;raw(*args, stream=None)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Raw write, no format (used by the report plugins).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;critical / error / warning / info / debug&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Standard levels. Every method accepts &lt;code&gt;exc=&lt;/code&gt; to pass an exception and format it.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;test_name(msg)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Custom level: announces the running test (&lt;code&gt;logger.test_name(test.name)&lt;/code&gt;).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;success(msg)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Custom level: passed assertion (used by the &lt;code&gt;.success(...)&lt;/code&gt; handlers).&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;exception(msg, exc=)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Logs an exception with the full traceback.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;cleanup()&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;End-of-life: flush + close (for &lt;code&gt;FileLogger&lt;/code&gt;). Recycles the log file between retries.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;three-things-that-are-not-in-ilogger&#34;&gt;Three things that are &lt;em&gt;not&lt;/em&gt; in &lt;code&gt;ILogger&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#three-things-that-are-not-in-ilogger&#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;No &lt;code&gt;set_level()&lt;/code&gt;&lt;/strong&gt;: level handling is the implementation&amp;rsquo;s call (e.g., &lt;code&gt;MutedLogger&lt;/code&gt; filters everything).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;No &lt;code&gt;add_handler()&lt;/code&gt;&lt;/strong&gt;: no &lt;code&gt;logging.Logger&lt;/code&gt;-style stdlib API. Composition happens by instantiating different loggers (&lt;code&gt;PrintAndFileLogger&lt;/code&gt; wraps both).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;No &lt;code&gt;child(name)&lt;/code&gt;&lt;/strong&gt;: taxonomy is passed &lt;em&gt;as a block&lt;/em&gt; via &lt;code&gt;set_domain_taxonomy&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;set_prefixthunk&#34;&gt;&lt;code&gt;set_prefix(thunk)&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#set_prefixthunk&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;logger&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;set_prefix(&lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;[&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;datetime&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;now()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;isoformat()&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;]&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A &lt;code&gt;str&lt;/code&gt; would be computed &lt;em&gt;at &lt;code&gt;set_prefix&lt;/code&gt; time&lt;/em&gt; and frozen. A &lt;code&gt;Thunk&lt;/code&gt; recomputes the prefix &lt;em&gt;on every log call&lt;/em&gt;. Perfect for timestamps.&lt;/p&gt;</description>
    </item>
    <item>
      <title>07.09 — catch_me_if_you_can</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/09-watcher-catch-me/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/09-watcher-catch-me/</guid>
      <description>&lt;h1 id=&#34;0709catch_me_if_you_can&#34;&gt;07.09 — &lt;code&gt;catch_me_if_you_can&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#0709catch_me_if_you_can&#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;Detects parasite elements that pop on the page &lt;em&gt;while&lt;/em&gt; the test is running, and traces them.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;callback&#34;&gt;Callback&lt;a class=&#34;anchor&#34; href=&#34;#callback&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/lib/ext/selenium/watchers/catch_me_if_you_can_watcher.py&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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;catch_me_if_you_can_cb&lt;/span&gt;(watcher: SeleniumWatcher) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    elements &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; watcher&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;driver&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute_script(&#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;return Array.from(document.querySelectorAll(&amp;#39;.catch-me-if-you-can&amp;#39;));&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;&#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; &lt;span style=&#34;color:#ff79c6&#34;&gt;not&lt;/span&gt; elements:&#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;return&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;    raw &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; watcher&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;driver&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute_script(&#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;&amp;#34;&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;        return arguments[0].map(el =&amp;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;            tag:       el.tagName.toLowerCase(),&#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;            text:      el.innerText.trim(),&#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;            id:        el.id,&#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;            cls:       el.className,&#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;            name:      el.getAttribute(&amp;#39;name&amp;#39;) || &amp;#39;&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;            testid:    el.getAttribute(&amp;#39;data-testid&amp;#39;) || &amp;#39;&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;        }));&#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;&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;        elements,&#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;&#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;for&lt;/span&gt; attrs &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; raw:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fingerprint &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;join(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;filter&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;None&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;                    attrs[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;tag&amp;#34;&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    attrs[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    attrs[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    attrs[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;cls&amp;#34;&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    attrs[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    attrs[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;testid&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;            )&#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;&#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; fingerprint &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; watcher&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;cache:&#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;continue&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;        watcher&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;cache&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;add(fingerprint)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        watcher&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;report(&#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;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;catch-me-if-you-can element detected: &amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;attrs[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;tag&amp;#39;&lt;/span&gt;]&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;attrs[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;text&amp;#39;&lt;/span&gt;]&lt;span style=&#34;color:#f1fa8c&#34;&gt;!r}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            label&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;CATCH_ME_IF_YOU_CAN&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;h2 id=&#34;mechanics&#34;&gt;Mechanics&lt;a class=&#34;anchor&#34; href=&#34;#mechanics&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;1-javascript-to-bypass-implicit-wait&#34;&gt;1. Javascript to bypass implicit wait&lt;a class=&#34;anchor&#34; href=&#34;#1-javascript-to-bypass-implicit-wait&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;elements &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; watcher&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;driver&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute_script(&#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;return Array.from(document.querySelectorAll(&amp;#39;.catch-me-if-you-can&amp;#39;));&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;Not &lt;code&gt;find_elements(By.CSS_SELECTOR, ...)&lt;/code&gt;.&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>
    <item>
      <title>09.03.09 — Setup skills</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/09-setup/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/09-setup/</guid>
      <description>&lt;h1 id=&#34;090309setup-skills&#34;&gt;09.03.09 — Setup skills&lt;a class=&#34;anchor&#34; href=&#34;#090309setup-skills&#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 skills: &lt;code&gt;setup-environment&lt;/code&gt; (onboarding a new contributor, human or AI) and &lt;code&gt;profile-environment&lt;/code&gt; (governing how much latitude the engagement grants the LLM). Both feed the suite&amp;rsquo;s assembled &lt;code&gt;CLAUDE.md&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;the-skill&#34;&gt;The skill&lt;a class=&#34;anchor&#34; href=&#34;#the-skill&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;input  : a fresh repo&#xA;output : onboarding steps:&#xA;            1. venv&#xA;            2. pip install (dev deps + ocarina)&#xA;            3. ruff / mypy / pre-commit installed&#xA;            4. CLAUDE.local.md created (paths requested from user)&#xA;            5. runner smoke-check (minimal test that validates everything works)&#xA;            6. pre-commit loop tested&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;step-1-venv&#34;&gt;Step 1: venv&lt;a class=&#34;anchor&#34; href=&#34;#step-1-venv&#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-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python -m venv .venv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;source&lt;/span&gt; .venv/bin/activate&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;step-2-pip-install&#34;&gt;Step 2: pip install&lt;a class=&#34;anchor&#34; href=&#34;#step-2-pip-install&#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-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install . ruff mypy mypy-extensions typing-extensions pre-commit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;step-3-dev-tooling&#34;&gt;Step 3: dev tooling&lt;a class=&#34;anchor&#34; href=&#34;#step-3-dev-tooling&#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-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pre-commit install --config .pre-commit-config.yaml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;step-4-claudelocalmd&#34;&gt;Step 4: &lt;code&gt;CLAUDE.local.md&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#step-4-claudelocalmd&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;CLAUDE.local.md&lt;/code&gt; is &lt;strong&gt;gitignored&lt;/strong&gt; and contains machine-specific paths.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.09 — Ecosystem coherence</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/09-ecosystem-coherence/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/09-ecosystem-coherence/</guid>
      <description>&lt;h1 id=&#34;1209ecosystem-coherence&#34;&gt;12.09 — Ecosystem coherence&lt;a class=&#34;anchor&#34; href=&#34;#1209ecosystem-coherence&#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 repos, several languages, several technologies, several licenses, several deployment platforms. At first glance, heterogeneous. On a second read, &lt;strong&gt;rigorously coherent&lt;/strong&gt;: every piece serves &lt;em&gt;exactly the same&lt;/em&gt; bet.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;1-overview-six-repos-one-ethic&#34;&gt;1. Overview: six repos, one ethic&lt;a class=&#34;anchor&#34; href=&#34;#1-overview-six-repos-one-ethic&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;┌────────────────────────────────────────────────────────────────────────┐&#xA;│         THE ECOSYSTEM AS A COHERENT WHOLE                              │&#xA;└────────────────────────────────────────────────────────────────────────┘&#xA;&#xA;  ┌──────────────┐    The framework. ROP, strict types, refusal of pytest&#xA;  │   ocarina    │    plugin. ONE single runtime dep. Auditable in an&#xA;  └─────────────┬┘    afternoon. — PROVES that rigor is lightweight.&#xA;                │&#xA;                │&#xA;                ▼&#xA;  ┌─────────────────┐ The reference of use. Project adapters. Canonical&#xA;  │ ocarina-example │ patterns. Shows how to narrow the framework&amp;#39;s API&#xA;  └─────────────┬───┘ surface for your context. — PROVES the framework is&#xA;                │     usable &amp;#34;by anyone&amp;#34;.&#xA;                │&#xA;                ▼&#xA;  ┌─────────────────────────┐ The AI bet. Co-written by Claude 99%. CURA&#xA;  │ ocarina-with-ai-example │ Healthcare as SUT. Findings sourced down to PHP.&#xA;  └─────────────┬───────────┘ — PROVES AI + Ocarina can do real senior-tester&#xA;                │               work.&#xA;                ▼&#xA;       ┌──────────────┐ The deliberately chaotic SUT. The playground.&#xA;       │  igoristan   │ — PROVES you can test a pathological app without&#xA;       └────────┬─────┘   cheating.&#xA;                │&#xA;                ▼&#xA;       ┌────────────────┐ The coordination backend. Vercel Edge,&#xA;       │ tests-workers  │ Upstash Redis, deliberate anti-precision.&#xA;       └────────┬───────┘ — PROVES you can do distributed coordination&#xA;                │           without a proprietary platform.&#xA;                │&#xA;                ▼&#xA;       ┌───────────────────┐ The public documentation. VitePress + FR/EN/RU&#xA;       │ ocarina-holy-book │ + LLM-first (llms.txt, CLAUDE.md, skills).&#xA;       └───────────────────┘ — PROVES you can write docs that serve human AND&#xA;                               AI, without a third-party platform.&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;2-one-repo--one-type-of-challenge&#34;&gt;2. One repo = one type of challenge&lt;a class=&#34;anchor&#34; href=&#34;#2-one-repo--one-type-of-challenge&#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;Repo&lt;/th&gt;&#xA;          &lt;th&gt;Type of challenge&lt;/th&gt;&#xA;          &lt;th&gt;Bet proven&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;ocarina&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Build a small and deep framework&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;Rigor isn&amp;rsquo;t a gas factory&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina-example&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Demonstrate canonical usage&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;You can&lt;/em&gt; actually &lt;em&gt;write rigorous tests like this&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina-with-ai-example&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Make a human and an AI collaborate&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;AI is the bridge, not the DSL&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;igoristan&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Build a &lt;em&gt;chaotic&lt;/em&gt; SUT&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;Not a demo on a twenty-HTML-line site — a demo on a real mess&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;tests-workers&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Be the substrate testers coordinate through&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;You can be &lt;em&gt;stateless&lt;/em&gt; on the code side&amp;rdquo;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;ocarina-holy-book&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Document for humans &lt;em&gt;and&lt;/em&gt; AI&lt;/td&gt;&#xA;          &lt;td&gt;&amp;ldquo;&lt;em&gt;Documentation is first and foremost raw data for LLMs&lt;/em&gt;&amp;rdquo;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Remove one of these repos and the ecosystem &lt;strong&gt;loses an argument&lt;/strong&gt;. The six are &lt;strong&gt;solidary&lt;/strong&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>07.10 — EnvGetters</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/10-env-getters/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/10-env-getters/</guid>
      <description>&lt;h1 id=&#34;0710envgetters&#34;&gt;07.10 — &lt;code&gt;EnvGetters&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#0710envgetters&#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;&lt;strong&gt;Typed&lt;/strong&gt; accessor for environment variables. Avoids typos, refuses unknown keys at compile time, brings IDE autocomplete.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/lib/ext/ocarina/adapters/agnostic/env_getters.py&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;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; Literal&#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;from&lt;/span&gt; types &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; MappingProxyType&#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;from&lt;/span&gt; ocarina.opinionated.infra.env &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; EnvGetters, Effects&#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:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; _CredsKeys &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Literal[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;dashboard&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:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; _ValuesKeys &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Literal[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;igor_api_key&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;redis_url&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;&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_load_env&lt;/span&gt;() &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;from&lt;/span&gt; dotenv &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; load_dotenv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    load_dotenv()&#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;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_DEFAULT_EFFECTS &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (_load_env,)&#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;&#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;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_EnvGetters&lt;/span&gt;(EnvGetters[_CredsKeys, _ValuesKeys]):&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, effects: Effects) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&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;for&lt;/span&gt; effect &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; effects:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            effect()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;super&lt;/span&gt;()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#50fa7b&#34;&gt;__init__&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            credentials&lt;span style=&#34;color:#ff79c6&#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:#f1fa8c&#34;&gt;&amp;#34;dashboard&amp;#34;&lt;/span&gt;: MappingProxyType({&#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;login&amp;#34;&lt;/span&gt;: os&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;environ[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;DASH_USERNAME&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;password&amp;#34;&lt;/span&gt;: os&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;environ[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;DASH_PASSWORD&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;            },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            values&lt;span style=&#34;color:#ff79c6&#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:#f1fa8c&#34;&gt;&amp;#34;igor_api_key&amp;#34;&lt;/span&gt;: os&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;environ[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;IGOR_API_KEY&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;redis_url&amp;#34;&lt;/span&gt;: os&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;environ[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;REDIS_URL&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;        )&#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;&#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;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;create_env_getters&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;, effects: Effects &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; _EnvGetters:&#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; effects &lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        effects &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; _DEFAULT_EFFECTS&#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;return&lt;/span&gt; _EnvGetters(effects&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;effects)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;_credskeys-and-_valueskeys&#34;&gt;&lt;code&gt;_CredsKeys&lt;/code&gt; and &lt;code&gt;_ValuesKeys&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#_credskeys-and-_valueskeys&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; _CredsKeys &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Literal[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;dashboard&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:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; _ValuesKeys &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Literal[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;igor_api_key&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;redis_url&amp;#34;&lt;/span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Credentials&lt;/strong&gt;: login/password pairs (immutable dict via &lt;code&gt;MappingProxyType&lt;/code&gt;).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Values&lt;/strong&gt;: raw values.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;mappingproxytype&#34;&gt;&lt;code&gt;MappingProxyType&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#mappingproxytype&#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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;dashboard&amp;#34;&lt;/span&gt;: MappingProxyType({&#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;login&amp;#34;&lt;/span&gt;: os&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;environ[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;DASH_USERNAME&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;password&amp;#34;&lt;/span&gt;: os&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;environ[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;DASH_PASSWORD&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;&lt;code&gt;MappingProxyType&lt;/code&gt; is a read-only wrapper around a dict:&lt;/p&gt;</description>
    </item>
    <item>
      <title>09.03.10 — Run skills</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/10-run/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/09-holy-book/03-skills/10-run/</guid>
      <description>&lt;h1 id=&#34;090310run-skills&#34;&gt;09.03.10 — Run skills&lt;a class=&#34;anchor&#34; href=&#34;#090310run-skills&#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;Surface the choices to make &lt;em&gt;before&lt;/em&gt; a local dispatch, compose the command, and hand it back. The skill never launches the run itself.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;listing-potentially-non-exhaustive&#34;&gt;Listing (potentially non-exhaustive)&lt;a class=&#34;anchor&#34; href=&#34;#listing-potentially-non-exhaustive&#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;Skill&lt;/th&gt;&#xA;          &lt;th&gt;Target&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;propose-visual-review&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Offer headed (&lt;code&gt;--not-headless&lt;/code&gt;) vs headless (CI-shaped) before a run, then compose the command&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;propose-visual-review&#34;&gt;&lt;code&gt;propose-visual-review&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#propose-visual-review&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;input  : an upcoming local run&#xA;output : the headed vs headless choice:&#xA;            - --not-headless: watch the browser, useful when debugging a flaky flow&#xA;            - headless (CI-shaped): faster, matches what CI does&#xA;         + the trade-off and what to watch for during a headed run&#xA;         + the composed command, handed back for the user to launch&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;role&#34;&gt;Role&lt;a class=&#34;anchor&#34; href=&#34;#role&#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;Surface&lt;/strong&gt; the pre-run choice; explain the trade-off.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Compose&lt;/strong&gt; the command.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Don&amp;rsquo;t launch&lt;/strong&gt; it — the user runs it.&lt;/li&gt;&#xA;&lt;/ul&gt;</description>
    </item>
    <item>
      <title>12.10 — Infopreneurs, Tugan Bara, AI</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/10-infopreneurs-tugan-bara-ai/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/10-infopreneurs-tugan-bara-ai/</guid>
      <description>&lt;h1 id=&#34;1210infopreneurs-tugan-bara-ai&#34;&gt;12.10 — Infopreneurs, Tugan Bara, AI&lt;a class=&#34;anchor&#34; href=&#34;#1210infopreneurs-tugan-bara-ai&#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 ecosystem &lt;em&gt;opposite&lt;/em&gt; to Ocarina. Not another testing framework: another &lt;em&gt;cultural model&lt;/em&gt;. We have to name what the Holy Book fights to understand &lt;em&gt;why&lt;/em&gt; it fights. And why AI is killing this model.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;1-what-is-infopreneurship&#34;&gt;1. What is &amp;ldquo;&lt;em&gt;infopreneurship&lt;/em&gt;&amp;rdquo;&lt;a class=&#34;anchor&#34; href=&#34;#1-what-is-infopreneurship&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;definition&#34;&gt;Definition&lt;a class=&#34;anchor&#34; href=&#34;#definition&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;Infopreneur&lt;/strong&gt; = entrepreneur whose product is &lt;em&gt;sold information&lt;/em&gt;: online courses, coaching programs, e-books, masterminds, &amp;ldquo;&lt;em&gt;premium newsletter&lt;/em&gt;&amp;rdquo; subscriptions. The product isn&amp;rsquo;t physical.&lt;/p&gt;&#xA;&lt;p&gt;Typical business model:&lt;/p&gt;</description>
    </item>
    <item>
      <title>07.11 — CI</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/11-ci/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/07-ocarina-example/11-ci/</guid>
      <description>&lt;h1 id=&#34;0711ci&#34;&gt;07.11 — CI&lt;a class=&#34;anchor&#34; href=&#34;#0711ci&#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 workflows: &lt;code&gt;main_ci.yml&lt;/code&gt; (fast, lint + typecheck), &lt;code&gt;dev_ci.yml&lt;/code&gt; (same for dev/feature/fix branches), &lt;code&gt;e2e.yml&lt;/code&gt; (manual, Redis service + Firefox).&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;a class=&#34;anchor&#34; href=&#34;#overview&#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;Workflow&lt;/th&gt;&#xA;          &lt;th&gt;Trigger&lt;/th&gt;&#xA;          &lt;th&gt;Platform&lt;/th&gt;&#xA;          &lt;th&gt;Effect&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;main_ci.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;push/PR main&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu + windows × py 3.14&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;make check-coding-style&lt;/code&gt; (mypy + ruff). &lt;em&gt;No browser.&lt;/em&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;dev_ci.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;push dev / feature/_ / fix/_&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu × py 3.14&lt;/td&gt;&#xA;          &lt;td&gt;id., but Ubuntu only&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;e2e.yml&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;workflow_dispatch&lt;/code&gt; (manual)&lt;/td&gt;&#xA;          &lt;td&gt;ubuntu + Redis service + Firefox + geckodriver&lt;/td&gt;&#xA;          &lt;td&gt;Full run of the e2e suite. Upload &lt;code&gt;.screenshots/&lt;/code&gt;, &lt;code&gt;.ocarina_logs/&lt;/code&gt;, &lt;code&gt;.reports/&lt;/code&gt;.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;main_ciyml&#34;&gt;&lt;code&gt;main_ci.yml&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#main_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;: Main branch 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;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;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;test&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;: ${{ matrix.os }}&#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;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;os&lt;/span&gt;: [ubuntu-latest, windows-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;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;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@v6&#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@v6&#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;: { &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;uses&lt;/span&gt;: actions/cache@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;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 }}-py3.14-${{ 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&#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;: make install-on-ci&#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;: Check coding style&#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;          . .venv/bin/activate&#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;          make check-coding-style&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;OS matrix&lt;/strong&gt;: ubuntu + windows — verifies Python portability.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;No browser&lt;/strong&gt;: just &lt;code&gt;make check-coding-style&lt;/code&gt; (mypy + ruff). Fast, no Selenium / Redis / OTP.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;.venv&lt;/code&gt; cache&lt;/strong&gt;: reused between runs.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;dev_ciyml&#34;&gt;&lt;code&gt;dev_ci.yml&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#dev_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;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;: [dev, &amp;#34;feature/**&amp;#34;, &amp;#34;fix/**&amp;#34;]&#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;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;test&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;    &lt;span style=&#34;color:#6272a4&#34;&gt;# … identical to main_ci.yml, just Ubuntu only …&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Identical to &lt;code&gt;main_ci.yml&lt;/code&gt; but Ubuntu only (Windows only runs on main).&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.11 — The SaaS industry, ARR fraud, the con</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/11-saas-arr-fraud/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/11-saas-arr-fraud/</guid>
      <description>&lt;h1 id=&#34;1211the-saas-industry-arr-fraud-the-con&#34;&gt;12.11 — The SaaS industry, ARR fraud, the con&lt;a class=&#34;anchor&#34; href=&#34;#1211the-saas-industry-arr-fraud-the-con&#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;Ocarina &lt;strong&gt;rejects SaaS&lt;/strong&gt;. The SaaS industry is a &lt;strong&gt;circular ecosystem&lt;/strong&gt; where every tool sells to every other tool, where declared revenue is inflated by convention, and where every layer of the capital chain (LP, VC, founder) deliberately maintains a &lt;strong&gt;plausible deniability&lt;/strong&gt; so as to sue the others when the time comes.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;1-contemporary-saas&#34;&gt;1. Contemporary SaaS&lt;a class=&#34;anchor&#34; href=&#34;#1-contemporary-saas&#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;Layer&lt;/th&gt;&#xA;          &lt;th&gt;Providers (sample)&lt;/th&gt;&#xA;          &lt;th&gt;Model&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Hosting &amp;amp; CDN&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Vercel, Netlify, Cloudflare, Fly.io, Render&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Subscription per project / per bandwidth&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;DB&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Supabase, Neon, PlanetScale, Upstash, MongoDB Atlas&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Subscription per compute / per GB&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Auth&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Clerk, Auth0, WorkOS, Stytch&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Subscription per MAU&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Mail&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Resend, SendGrid, Postmark, Mailgun&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Subscription per sent email&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Analytics&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;PostHog, Mixpanel, Amplitude, Plausible, Vercel Analytics&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Subscription per event&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Error tracking&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Sentry, BugSnag, Rollbar, Highlight&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Subscription per event&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Observability&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Datadog, New Relic, Honeycomb, Grafana Cloud, Axiom&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Subscription per metric volume&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Feature flags&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;LaunchDarkly, GrowthBook, Statsig, ConfigCat&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Subscription per MAU&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;E2E test&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;BrowserStack, Sauce Labs, LambdaTest, Cypress Dashboard&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Subscription per run / per parallel&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;CI&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;GitHub Actions, CircleCI, Buildkite, Vercel CI&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Subscription per minute&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Code review&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Graphite, Codeball, CodeRabbit, Sourcery&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Subscription per PR / per dev&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Internal comms&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Slack, Linear, Notion, Figma, Loom, Pitch&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Subscription per seat&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Billing&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Stripe, Lago, Orb, Metronome&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;% of processed revenue&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;CRM&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;HubSpot, Salesforce, Attio, Pipedrive&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Subscription per seat&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Support&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Intercom, Zendesk, Plain, Help Scout&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Subscription per seat + per message&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;AI tooling&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;OpenAI, Anthropic, Cursor, Vercel AI SDK, Replicate&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Subscription per token + per seat&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;2-cross-subscriptions&#34;&gt;2. Cross-subscriptions&lt;a class=&#34;anchor&#34; href=&#34;#2-cross-subscriptions&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;   ┌──────────┐ pays     ┌──────────┐&#xA;   │ Vercel   │─────────&amp;gt;│ Stripe   │&#xA;   │          │&amp;lt;─────────│          │&#xA;   └──────────┘     pays └──────────┘&#xA;        │                     │&#xA;   pays │                     │ pays&#xA;        v                     v&#xA;   ┌──────────┐          ┌──────────┐&#xA;   │ Linear   │&amp;lt;────────&amp;gt;│ Notion   │&#xA;   └──────────┘   pay    └──────────┘&#xA;        │                     │&#xA;        v                     v&#xA;   ┌───────────────────────────────┐&#xA;   │ Slack, Figma, Loom, Sentry,   │&#xA;   │ Datadog, Clerk, Cursor, …     │&#xA;   │ all subscribed to all         │&#xA;   └───────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Vercel&lt;/strong&gt; pays a subscription to &lt;em&gt;Stripe&lt;/em&gt; (billing), &lt;em&gt;Datadog&lt;/em&gt; (obs), &lt;em&gt;Linear&lt;/em&gt; (planning), &lt;em&gt;Figma&lt;/em&gt; (design), &lt;em&gt;Loom&lt;/em&gt; (internal presentations), &lt;em&gt;Slack&lt;/em&gt; (comms), &lt;em&gt;Notion&lt;/em&gt; (docs).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Stripe&lt;/strong&gt; pays a subscription to &lt;em&gt;Vercel&lt;/em&gt; (hosting for its dashboards), &lt;em&gt;Linear&lt;/em&gt;, &lt;em&gt;Figma&lt;/em&gt;, &lt;em&gt;Slack&lt;/em&gt;…&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Linear&lt;/strong&gt; pays a subscription to &lt;em&gt;Vercel&lt;/em&gt;, &lt;em&gt;Stripe&lt;/em&gt;, &lt;em&gt;Notion&lt;/em&gt;, &lt;em&gt;Figma&lt;/em&gt;, &lt;em&gt;Slack&lt;/em&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Notion&lt;/strong&gt; pays a subscription to &lt;em&gt;Linear&lt;/em&gt;, &lt;em&gt;Stripe&lt;/em&gt;, &lt;em&gt;Slack&lt;/em&gt;, &lt;em&gt;Figma&lt;/em&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;And so on. &lt;strong&gt;Everyone pays everyone.&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>02.12 — Custom types &amp; custom errors</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/12-custom-types-errors/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/02-ocarina/12-custom-types-errors/</guid>
      <description>&lt;h1 id=&#34;0212custom-types--custom-errors&#34;&gt;02.12 — Custom types &amp;amp; custom errors&lt;a class=&#34;anchor&#34; href=&#34;#0212custom-types--custom-errors&#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;Ocarina&amp;rsquo;s &lt;em&gt;shape&lt;/em&gt; layer. No logic — types, aliases, exceptions, protocols. What makes the DSL typed without adding runtime logic.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;custom_types&#34;&gt;&lt;code&gt;custom_types/&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#custom_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;File&lt;/th&gt;&#xA;          &lt;th&gt;Contents&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;effect.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;type Effect = Callable[[], None]&lt;/code&gt; + &lt;code&gt;type Effects = tuple[Effect, ...]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;thunk.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;type Thunk[T] = Callable[[], T]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;tpom.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TypeVar TPOM bound POMBase&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;supports_write.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Protocol SupportsWrite[T]&lt;/code&gt; (&lt;code&gt;write(s: T) -&amp;gt; Any&lt;/code&gt;)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;built_web_driver.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;type BuiltWebDriver[Driver] = tuple[Driver, Effect]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;scenario.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Scenario[Driver]&lt;/code&gt; (frozen dataclass)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;test_components.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestChain&lt;/code&gt;, &lt;code&gt;TestSetup&lt;/code&gt;, &lt;code&gt;TestTeardown&lt;/code&gt;, &lt;code&gt;TestWatchers[Driver]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;test_runner.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestRunner[Driver]&lt;/code&gt; (frozen dataclass)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;oc_test.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestName&lt;/code&gt;, &lt;code&gt;TestScenario[Driver]&lt;/code&gt;, &lt;code&gt;TestScenarioFragment[Driver]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;oc_test_layers.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;TestId&lt;/code&gt;, &lt;code&gt;TestResult&lt;/code&gt;, &lt;code&gt;TestSuiteResult&lt;/code&gt;, &lt;code&gt;TestSuiteResults&lt;/code&gt;, &lt;code&gt;TestCampaignResults&lt;/code&gt;, &lt;code&gt;TestCycleResults&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;selenium/built_web_driver.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;BuiltSeleniumWebDriver = BuiltWebDriver[WebDriver]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;selenium/oc_test_scenario.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;SeleniumTestScenario = TestScenario[WebDriver]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;selenium/supported_browsers.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;type SupportedSeleniumBrowser = Literal[&amp;quot;chrome&amp;quot;, &amp;quot;firefox&amp;quot;, &amp;quot;edge&amp;quot;, &amp;quot;safari&amp;quot;]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;selenium/web_drivers_pool.py&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;type SeleniumWebDriversPool = WebDriversPool[WebDriver]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;All these files are &lt;em&gt;excluded from coverage&lt;/em&gt; (&lt;code&gt;pyproject.toml#tool.coverage&lt;/code&gt;):&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.12 — DHH, Paul Graham, Haters, Mean People Fail</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/12-dhh-paul-graham-haters-mean/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/12-dhh-paul-graham-haters-mean/</guid>
      <description>&lt;h1 id=&#34;1212dhh-paul-graham-haters-mean-people-fail&#34;&gt;12.12 — DHH, Paul Graham, &lt;em&gt;Haters&lt;/em&gt;, &lt;em&gt;Mean People Fail&lt;/em&gt;&lt;a class=&#34;anchor&#34; href=&#34;#1212dhh-paul-graham-haters-mean-people-fail&#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 Holy Book quotes two big names: &lt;strong&gt;David Heinemeier Hansson (DHH)&lt;/strong&gt; and &lt;strong&gt;Paul Graham (pg)&lt;/strong&gt;. These two authors aren&amp;rsquo;t cited at random; each embodies a precise &lt;strong&gt;rhetorical posture&lt;/strong&gt;. This file explains who they are, what they wrote, and why Paul Graham&amp;rsquo;s &lt;em&gt;Haters&lt;/em&gt; essay (and its younger sibling &lt;em&gt;Mean People Fail&lt;/em&gt;) is the direct &lt;em&gt;intellectual precedent&lt;/em&gt; of the &amp;ldquo;&lt;em&gt;First feedbacks&lt;/em&gt;&amp;rdquo; chapter.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.13 — Lambda calculus, ROP, Haskell / F# / OCaml, monads</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/13-lambda-calculus-rop-origins/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/13-lambda-calculus-rop-origins/</guid>
      <description>&lt;h1 id=&#34;1213lambda-calculus-rop-haskell--f--ocaml-monads&#34;&gt;12.13 — Lambda calculus, ROP, Haskell / F# / OCaml, monads&lt;a class=&#34;anchor&#34; href=&#34;#1213lambda-calculus-rop-haskell--f--ocaml-monads&#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;Ocarina invents nothing on the theoretical front. It &lt;strong&gt;applies&lt;/strong&gt;: from Alonzo Church&amp;rsquo;s &lt;strong&gt;lambda calculus&lt;/strong&gt; (1936) to Eugenio Moggi&amp;rsquo;s (1989) and Philip Wadler&amp;rsquo;s (1992-95) &lt;strong&gt;monads&lt;/strong&gt;, formalized as &lt;strong&gt;Railway Oriented Programming&lt;/strong&gt; by Scott Wlaschin (2014).&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;1-lambda-calculus-church-1936&#34;&gt;1. Lambda calculus (Church, 1936)&lt;a class=&#34;anchor&#34; href=&#34;#1-lambda-calculus-church-1936&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;definition&#34;&gt;Definition&lt;a class=&#34;anchor&#34; href=&#34;#definition&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;Lambda calculus&lt;/strong&gt; (λ-calculus) is a formal system invented by &lt;strong&gt;Alonzo Church&lt;/strong&gt; (1903-1995) to study &lt;strong&gt;computability&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Primitive&lt;/th&gt;&#xA;          &lt;th&gt;Notation&lt;/th&gt;&#xA;          &lt;th&gt;Semantics&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;Variable&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, &lt;code&gt;z&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;A name&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Abstraction&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;λx.M&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Definition of a function with parameter &lt;code&gt;x&lt;/code&gt;, body &lt;code&gt;M&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Application&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;M N&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Call of the function &lt;code&gt;M&lt;/code&gt; on the argument &lt;code&gt;N&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Rule&lt;/th&gt;&#xA;          &lt;th&gt;Effect&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;α-conversion&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Renaming a bound variable (&lt;code&gt;λx.x&lt;/code&gt; ≡ &lt;code&gt;λy.y&lt;/code&gt;)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;β-reduction&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Applying a function: &lt;code&gt;(λx.M) N → M[x := N]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;η-conversion&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;λx.(f x) ≡ f&lt;/code&gt; if &lt;code&gt;x&lt;/code&gt; not free in &lt;code&gt;f&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h3 id=&#34;why-its-foundational&#34;&gt;Why it&amp;rsquo;s foundational&lt;a class=&#34;anchor&#34; href=&#34;#why-its-foundational&#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;1936&lt;/strong&gt;: Church proves λ-calculus is &lt;strong&gt;Turing-complete&lt;/strong&gt; (Turing publishes his machine in 1937). &lt;em&gt;Church-Turing thesis&lt;/em&gt;: anything &lt;em&gt;mechanically computable&lt;/em&gt; can be expressed in λ-calculus.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Alan Turing&lt;/strong&gt; was Church&amp;rsquo;s doctoral student at Princeton (PhD, 1938).&lt;/li&gt;&#xA;&lt;li&gt;λ-calculus is &lt;strong&gt;simpler&lt;/strong&gt; than the Turing machine: only 3 constructions. Everything else — integers, booleans, data structures — is &lt;em&gt;encoded&lt;/em&gt; with those 3 bricks (Church encoding).&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;typed-lambda-calculus-church-1940&#34;&gt;Typed lambda calculus (Church, 1940)&lt;a class=&#34;anchor&#34; href=&#34;#typed-lambda-calculus-church-1940&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;Church added &lt;strong&gt;types&lt;/strong&gt; in 1940 (&lt;em&gt;simply typed lambda calculus&lt;/em&gt;, STLC) to forbid self-application paradoxes (&lt;code&gt;λx.x x&lt;/code&gt;). Each variable has a type, and function application is only allowed if types match.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.14 — AI, and how Ocarina applies it for real</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/14-real-ai-in-ocarina/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/14-real-ai-in-ocarina/</guid>
      <description>&lt;h1 id=&#34;1214ai-and-how-ocarina-applies-it-for-real&#34;&gt;12.14 — AI, and how Ocarina applies it &lt;em&gt;for real&lt;/em&gt;&lt;a class=&#34;anchor&#34; href=&#34;#1214ai-and-how-ocarina-applies-it-for-real&#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;&amp;ldquo;&lt;em&gt;AI-powered&lt;/em&gt;&amp;rdquo; became a totally idiotic marketing slogan in 2023-2026: 99% of products claiming it bolt a &lt;code&gt;chat.completions&lt;/code&gt; endpoint onto an existing product. Ocarina proposes, on the contrary, a &lt;strong&gt;software infrastructure designed so an AI can contribute like a senior developer&lt;/strong&gt;. &lt;a href=&#34;https://github.com/mojo-molotov/ocarina-with-ai-example&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;ocarina-with-ai-example&lt;/code&gt;&lt;/a&gt; is the proof by example.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;1-what-ai-is-today&#34;&gt;1. What AI is today&lt;a class=&#34;anchor&#34; href=&#34;#1-what-ai-is-today&#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;Layer&lt;/th&gt;&#xA;          &lt;th&gt;Example&lt;/th&gt;&#xA;          &lt;th&gt;Appearance date&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;Statistical learning&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Regression, k-means, decision trees&lt;/td&gt;&#xA;          &lt;td&gt;1960s-1990s&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Deep learning&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;CNN (vision), RNN/LSTM (text)&lt;/td&gt;&#xA;          &lt;td&gt;2012 (AlexNet — mainstream adoption)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Transformer-based foundation models&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;GPT, Claude, Gemini, Llama&lt;/td&gt;&#xA;          &lt;td&gt;2017 (paper), 2018 (BERT), 2022 (ChatGPT)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Agentic workflows&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;AutoGPT, LangChain, Claude Code, Cursor agents&lt;/td&gt;&#xA;          &lt;td&gt;2023+&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;&lt;em&gt;Note: CNNs were invented by Yann LeCun as early as 1989 (MNIST). AlexNet (Krizhevsky, Sutskever, Hinton, Toronto, 2012) was the industrial breakthrough. LeCun, Hinton, and Bengio share the 2018 Turing Award for their foundational contributions to deep learning.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.15 — Typing, ISTQB, Reinforcement Learning, probes: why AI works in Ocarina</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/15-typing-ai-rl-probes/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/15-typing-ai-rl-probes/</guid>
      <description>&lt;h1 id=&#34;1215typing-istqb-reinforcement-learning-probes-why-ai-works-in-ocarina&#34;&gt;12.15 — Typing, ISTQB, Reinforcement Learning, &lt;em&gt;probes&lt;/em&gt;: why AI works in Ocarina&lt;a class=&#34;anchor&#34; href=&#34;#1215typing-istqb-reinforcement-learning-probes-why-ai-works-in-ocarina&#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;Why so much &lt;strong&gt;obsession&lt;/strong&gt; over strict typing, formalizing every interface, alignment on &lt;strong&gt;ISTQB&lt;/strong&gt;, refusing to execute an assertion without having &lt;em&gt;observed&lt;/em&gt;? The mechanism is borrowed from &lt;strong&gt;Reinforcement Learning&lt;/strong&gt;: an agent only progresses under &lt;strong&gt;constraint&lt;/strong&gt; + &lt;strong&gt;reward signal&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;1-a-naked-llm-is-just-a-bad-collaborator&#34;&gt;1. A naked LLM is just a bad collaborator&lt;a class=&#34;anchor&#34; href=&#34;#1-a-naked-llm-is-just-a-bad-collaborator&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;An LLM without a structured environment is:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;Optimistic&lt;/em&gt; — it assumes its code works.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;Persuasive&lt;/em&gt; — its &lt;em&gt;output&lt;/em&gt; is convincing even when wrong.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;Memoryless&lt;/em&gt; — it forgets conventions between sessions.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;Feedbackless&lt;/em&gt; — it has no way to know it was wrong.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;Loves what it deems &amp;ldquo;most likely&amp;rdquo;&lt;/em&gt; — it drifts toward &lt;em&gt;popular&lt;/em&gt; patterns, not &lt;em&gt;correct&lt;/em&gt; ones.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Without an environment, an LLM is an over-confident junior dev&lt;/strong&gt; producing code that &lt;em&gt;looks&lt;/em&gt; right, breaks in prod, and learns nothing from the failure.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.16 — The underground internet of 1995-2010: forums, tools, banalization of cruelty</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/16-underground-internet-2000s/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/16-underground-internet-2000s/</guid>
      <description>&lt;h1 id=&#34;1216the-underground-internet-of-1995-2010-forums-tools-banalization-of-cruelty&#34;&gt;12.16 — The underground internet of 1995-2010: forums, tools, banalization of cruelty&lt;a class=&#34;anchor&#34; href=&#34;#1216the-underground-internet-of-1995-2010-forums-tools-banalization-of-cruelty&#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;To understand the rage running through Ocarina&amp;rsquo;s Holy Book, you have to understand &lt;strong&gt;what the internet &lt;em&gt;really&lt;/em&gt; was&lt;/strong&gt; before Facebook (2006-2008), before Cloudflare became the &lt;em&gt;gatekeeper&lt;/em&gt; (2010+), before &lt;em&gt;moderation&lt;/em&gt; was taken seriously. It was a &lt;em&gt;Wild West&lt;/em&gt; where state-level capabilities developed in teenagers&amp;rsquo; bedrooms, where an adversary&amp;rsquo;s &lt;em&gt;privacy&lt;/em&gt; could be demolished in 48 hours, and where classical legal recourse was &lt;strong&gt;inoperative&lt;/strong&gt;. This page describes the mechanics, without complacency, without heroization.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.17 — The survivor&#39;s psyche: &#34;enemies&#34;, &#34;narcissism&#34;, &#34;hell&#34;, &#34;survival&#34;, and how it bubbles up into the code</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/17-survivor-psyche-programming/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/17-survivor-psyche-programming/</guid>
      <description>&lt;h1 id=&#34;1217the-survivors-psyche-enemies-narcissism-hell-survival-and-how-it-bubbles-up-into-the-code&#34;&gt;12.17 — The survivor&amp;rsquo;s psyche: &amp;ldquo;&lt;em&gt;enemies&lt;/em&gt;&amp;rdquo;, &amp;ldquo;&lt;em&gt;narcissism&lt;/em&gt;&amp;rdquo;, &amp;ldquo;&lt;em&gt;hell&lt;/em&gt;&amp;rdquo;, &amp;ldquo;&lt;em&gt;survival&lt;/em&gt;&amp;rdquo;, and how it bubbles up into the code&lt;a class=&#34;anchor&#34; href=&#34;#1217the-survivors-psyche-enemies-narcissism-hell-survival-and-how-it-bubbles-up-into-the-code&#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;Ocarina&amp;rsquo;s Holy Book author uses strong words: &lt;strong&gt;enemies&lt;/strong&gt;, &lt;strong&gt;narcissism&lt;/strong&gt;, &lt;strong&gt;hell on earth&lt;/strong&gt;, &lt;strong&gt;survival&lt;/strong&gt;. These words aren&amp;rsquo;t stylistic flourishes. They&amp;rsquo;re the &lt;strong&gt;precise descriptors&lt;/strong&gt; of an experience lived by a whole cohort that went through the 2000s underground internet described in &lt;a href=&#34;https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/16-underground-internet-2000s/&#34;&gt;&lt;code&gt;16-underground-internet-2000s.md&lt;/code&gt;&lt;/a&gt;. This page explains why these words are &lt;em&gt;accurate&lt;/em&gt;, what this lived experience produces on the psyche, and how these psychological adaptations imprint &lt;em&gt;deeply&lt;/em&gt; into the way one programs, architects, &lt;em&gt;always stays up to date&lt;/em&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.18 — &#34;Villains but not Monsters&#34;, DEF CON, &#34;live as a white hat or die as a black hat&#34;, and Ocarina&#39;s ideology</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/18-villains-defcon-ocarina-ideology/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/18-villains-defcon-ocarina-ideology/</guid>
      <description>&lt;h1 id=&#34;1218villains-but-not-monsters-def-con-live-as-a-white-hat-or-die-as-a-black-hat-and-ocarinas-ideology&#34;&gt;12.18 — &amp;quot;&lt;em&gt;Villains but not Monsters&lt;/em&gt;&amp;quot;, DEF CON, &amp;ldquo;&lt;em&gt;live as a white hat or die as a black hat&lt;/em&gt;&amp;rdquo;, and Ocarina&amp;rsquo;s ideology&lt;a class=&#34;anchor&#34; href=&#34;#1218villains-but-not-monsters-def-con-live-as-a-white-hat-or-die-as-a-black-hat-and-ocarinas-ideology&#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;Ocarina is neither an aesthetic whim nor an out-of-the-blue UFO. It&amp;rsquo;s the technical expression of a &lt;strong&gt;coherent ideology&lt;/strong&gt; inherited from the hacker movement: the &lt;strong&gt;Villain, but not a monster&lt;/strong&gt; ethic, the &lt;strong&gt;tribal culture&lt;/strong&gt; of the conferences (DEF CON foremost), and YTCracker&amp;rsquo;s &lt;strong&gt;stage motto&lt;/strong&gt;: &amp;ldquo;&lt;em&gt;live as a white hat, or die as a black hat&lt;/em&gt;&amp;rdquo;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>12.19 — It&#39;s over</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/19-its-over/</link>
      <pubDate>Sun, 31 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/12-manifesto-deciphered/19-its-over/</guid>
      <description>&lt;h1 id=&#34;1219its-over&#34;&gt;12.19 — It&amp;rsquo;s over&lt;a class=&#34;anchor&#34; href=&#34;#1219its-over&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;p&gt;I don&amp;rsquo;t write code that &amp;ldquo;implements specs.&amp;rdquo;&lt;br&gt;&#xA;I write code with &lt;strong&gt;a soul&lt;/strong&gt;.&lt;br&gt;&#xA;The real thing, made by someone who gives a damn. You have any idea what that means? While you look down your nose at us, while you call us autistic?&lt;/p&gt;&#xA;&lt;p&gt;We&amp;rsquo;re not asking for anything. Just respect, and the room to do our work like normal people.&lt;br&gt;&#xA;The state of this industry is exhausting.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
