<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Victor Augusteo</title><description>Personal blog about travels, tech, books, and philosophy</description><link>https://www.augusteo.com/</link><item><title>Inside Gas Town</title><link>https://www.augusteo.com/blog/inside-gas-town/</link><guid isPermaLink="true">https://www.augusteo.com/blog/inside-gas-town/</guid><description>An end-to-end, intuition-first explanation of Steve Yegge&apos;s multi-agent coding orchestrator, how it has evolved into Gas City, and what its design says about the next layer of developer tools.</description><pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;An end-to-end, intuition-first explanation of Steve Yegge&apos;s multi-agent coding orchestrator, how it has evolved into Gas City, and what its design says about the next layer of developer tools. About a 30-minute read.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Prologue: what must be made durable&lt;/h2&gt;
&lt;h3&gt;1. What a coding agent actually produces&lt;/h3&gt;
&lt;p&gt;The argument starts with a small reframing. When you run Claude Code on a task, it feels like the artifact is a diff. The agent reads, the agent thinks, the agent writes. You merge the diff and you&apos;re done.&lt;/p&gt;
&lt;p&gt;But the diff is not really what the agent produces. The diff is what a &lt;em&gt;session&lt;/em&gt; produces, using the agent. The agent itself produces something more like a trail: a sequence of decisions, lookups, mistakes, partial fixes, half-remembered constraints, scratch ideas about how to do the next part. The diff is a snapshot of where the trail happened to be when the context window ran out.&lt;/p&gt;
&lt;p&gt;This trail is mostly invisible to you, mostly invisible to the next session, and entirely lost when the context window fills up. The next session starts from zero. Whatever the previous session learned, it learned for the duration of one context window, and then it took it to the grave.&lt;/p&gt;
&lt;p&gt;Most of the friction in working with a single coding agent is downstream of this. The agent rediscovers facts you already taught it. It pursues approaches you already rejected. It writes the same broken function twice. It reaches the end of its context with the work two-thirds done and no good way to hand off. None of this is the model&apos;s fault, exactly. It&apos;s the medium.&lt;/p&gt;
&lt;p&gt;The job of an orchestrator (which is what Gas Town is) is to fix the medium. You can&apos;t preserve the cognitive trail itself — the LLM&apos;s scratch reasoning is gone the moment the context fills up, and no orchestrator gets that back. But you can preserve the &lt;em&gt;result&lt;/em&gt; of the trail: the work that&apos;s been done, the decisions that have been made explicit, the open questions a future session can pick up. Make the work survive the session, let multiple agents pass it between them without losing what&apos;s on it. Once you accept that as the goal, almost everything else in Gas Town&apos;s design follows.&lt;/p&gt;
&lt;h3&gt;2. The naive answer, and why it explodes&lt;/h3&gt;
&lt;p&gt;Suppose you accept that one Claude Code is not enough. You want twenty.&lt;/p&gt;
&lt;p&gt;The naive way to do this is &lt;code&gt;tmux new-window&lt;/code&gt; twenty times. Each window runs a fresh Claude Code in a separate worktree. Each one gets its own slice of work. You walk between them like a foreman, checking on each, prompting them when they get stuck, copying their output back to a master plan when they finish.&lt;/p&gt;
&lt;p&gt;This works for about an hour. Then it doesn&apos;t.&lt;/p&gt;
&lt;p&gt;Three things go wrong, all at the same time. First, the agents start stepping on each other. Two of them rebase against the same branch and pick different conflict resolutions, and now your repo has a private fork. Second, you forget what each window is doing. They all started with &lt;code&gt;let&apos;s go&lt;/code&gt;, and after fifty turns of dialog the windows look identical. You cannot remember which one was redoing the auth migration and which one was fixing the flaky test. Third, half the agents have hit context exhaustion and are sitting silently waiting for input, which they are not going to receive because you are busy talking to one of the other agents who is currently telling you a long story about why the database schema is wrong.&lt;/p&gt;
&lt;p&gt;Yegge&apos;s framing for the resulting environment is, by his own account, the project&apos;s namesake. Gas Town is &quot;an industrialized coding factory manned by superintelligent robot chimps, and when they feel like it, they can wreck your shit in an instant.&quot; The chimps are competing for tokens and breaking each other&apos;s work. You are armed only with a copy of &lt;code&gt;tmux send-keys&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 760 360&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; font-family=&quot;serif&quot; role=&quot;img&quot; aria-label=&quot;Four parallel agents trying to merge into main, with conflicts and waiting agents.&quot;&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;g transform=&quot;translate(40,30)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;150&quot; height=&quot;80&quot; fill=&quot;none&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;10&quot; y=&quot;20&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot;&amp;gt;polecat-1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;10&quot; y=&quot;40&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;$ git rebase main&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;10&quot; y=&quot;55&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;! conflict: auth.go&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;10&quot; y=&quot;70&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;push --force&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(220,30)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;150&quot; height=&quot;80&quot; fill=&quot;none&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;10&quot; y=&quot;20&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot;&amp;gt;polecat-2&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;10&quot; y=&quot;40&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;$ git rebase main&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;10&quot; y=&quot;55&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;! conflict: auth.go&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;10&quot; y=&quot;70&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;push --force&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(400,30)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;150&quot; height=&quot;80&quot; fill=&quot;none&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;10&quot; y=&quot;20&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot;&amp;gt;polecat-3&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;10&quot; y=&quot;40&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#8a8377&quot;&amp;gt;waiting for input&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;10&quot; y=&quot;55&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#8a8377&quot;&amp;gt;since 11 min ago&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;10&quot; y=&quot;70&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#8a8377&quot;&amp;gt;. . .&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(580,30)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;150&quot; height=&quot;80&quot; fill=&quot;none&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;10&quot; y=&quot;20&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot;&amp;gt;polecat-4&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;10&quot; y=&quot;40&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;explaining schema&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;10&quot; y=&quot;55&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;to user, going on&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;10&quot; y=&quot;70&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;third paragraph&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g stroke=&quot;#b8543e&quot; stroke-width=&quot;1.4&quot; fill=&quot;none&quot;&amp;gt;
&amp;lt;path d=&quot;M115,110 Q150,160 380,200&quot; stroke-dasharray=&quot;4 3&quot;/&amp;gt;
&amp;lt;path d=&quot;M295,110 Q310,160 380,200&quot; stroke-dasharray=&quot;4 3&quot;/&amp;gt;
&amp;lt;path d=&quot;M475,110 Q470,160 380,200&quot; stroke-dasharray=&quot;4 3&quot;/&amp;gt;
&amp;lt;path d=&quot;M655,110 Q580,160 380,200&quot; stroke-dasharray=&quot;4 3&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;200&quot; y=&quot;135&quot; font-style=&quot;italic&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;collisions&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;500&quot; y=&quot;135&quot; font-style=&quot;italic&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;silent timeouts&amp;lt;/text&amp;gt;
&amp;lt;g transform=&quot;translate(255,205)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;250&quot; height=&quot;60&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.8&quot; rx=&quot;2&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g stroke=&quot;#b8543e&quot; stroke-width=&quot;2&quot; opacity=&quot;0.5&quot;&amp;gt;
&amp;lt;line x1=&quot;290&quot; y1=&quot;220&quot; x2=&quot;490&quot; y2=&quot;255&quot;/&amp;gt;
&amp;lt;line x1=&quot;490&quot; y1=&quot;220&quot; x2=&quot;290&quot; y2=&quot;255&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(255,205)&quot;&amp;gt;
&amp;lt;text x=&quot;125&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;600&quot;&amp;gt;main&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;125&quot; y=&quot;42&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;two HEADs · history forked&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;380&quot; y=&quot;305&quot; text-anchor=&quot;middle&quot; font-style=&quot;italic&quot; font-size=&quot;13&quot; fill=&quot;#b8543e&quot;&amp;gt;&quot;the kid&apos;s soccer team chasing the ball&quot;&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;380&quot; y=&quot;330&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;no merge queue. no health monitor. no shared notion of what&apos;s done.&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 1.&amp;lt;/strong&amp;gt; What twenty parallel coding agents look like with no orchestrator. The work is happening; it is just not converging. The merge queue does not exist as a concept yet, so every agent thinks it owns main.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;The naive answer fails because it has no model of what the work is. The agents have no shared notion of what&apos;s done, what&apos;s queued, what&apos;s been merged, what&apos;s currently being attempted. There&apos;s no merge queue, no health monitor, no priority. There&apos;s just twenty independent processes shouting &lt;code&gt;git push --force&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You need a substrate. That&apos;s where Beads comes in.&lt;/p&gt;
&lt;h3&gt;3. Beads, or what work looks like when AI reads it&lt;/h3&gt;
&lt;p&gt;Beads is Yegge&apos;s earlier project, launched October 2025. It&apos;s a git-backed issue tracker. Originally each issue was a JSON object on its own line in a JSONL file kept in your repo, with a SQLite cache layer hydrating from that file for fast queries. As of v1.0 in April 2026, the storage layer is embedded Dolt, a git-versioned SQL database. The principle hasn&apos;t changed: work-as-data, in version control, queryable by an LLM. Just the substrate did.&lt;/p&gt;
&lt;p&gt;If you&apos;ve used Linear or Jira, this looks like a step backwards. You&apos;re trading a slick web UI for a CLI and some structured text in a directory called &lt;code&gt;.beads/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But there are two things Beads gets right that Linear and Jira can&apos;t.&lt;/p&gt;
&lt;p&gt;The first is that the issues are &lt;em&gt;in the repo&lt;/em&gt;. The work tracker lives where your code lives, version-controlled by git. In the original SQLite+JSONL Beads, this meant your branch and your work tracker moved together: checkout an old commit, the issues rewound to match; fork a branch, the work plan forked with it. Beads v1.0&apos;s default Classic mode currently keeps all issues on Dolt&apos;s &lt;code&gt;main&lt;/code&gt; branch regardless of your git branch position; restoring the original branch coupling is on the &lt;a href=&quot;https://www.dolthub.com/blog/2026-04-15-common-beads-workflows/&quot;&gt;Dolt team&apos;s roadmap&lt;/a&gt; but not implemented yet. Either way, the issues are queryable structured data your agent can read directly, with full history committed alongside your code, no API call required.&lt;/p&gt;
&lt;p&gt;The second is the data format. Whether the format is JSONL or a Dolt table, the agent gets structured records it can read, edit, append to, and version. There&apos;s no opaque database, no auth flow, no rate limit, no schema migration. The agent reads the work tracker the same way it reads your code: as queryable structured data, version-controlled by git.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Original Beads (Oct 2025 - early 2026): JSONL in your repo
{&quot;id&quot;:&quot;bd-a7je4&quot;,&quot;title&quot;:&quot;fix rate limiter race&quot;,&quot;status&quot;:&quot;ready&quot;,...}
{&quot;id&quot;:&quot;bd-a7je5&quot;,&quot;title&quot;:&quot;add backpressure metric&quot;,&quot;status&quot;:&quot;blocked&quot;,...}

# Beads v1.0 (Apr 2026): same shape, embedded Dolt as the store
SELECT id, title, status FROM beads WHERE assignee = &apos;sully&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the foundation. Everything else in Gas Town is built on the assumption that work is a thing you can read, edit, append to, and version, the same way you do with code. Once you accept that as the substrate, a number of moves that look weird start to make sense.&lt;/p&gt;
&lt;h3&gt;4. The unit-of-work inversion&lt;/h3&gt;
&lt;p&gt;Here&apos;s the second move, and it&apos;s the central one.&lt;/p&gt;
&lt;p&gt;A Claude Code session runs for a while and then ends. Either you &lt;code&gt;/exit&lt;/code&gt;, or the context runs out, or the host process gets killed. The session has a beginning and an end. It is, in Kubernetes terms, a pod: ephemeral, replaceable, anonymous. You don&apos;t name your pods.&lt;/p&gt;
&lt;p&gt;A pod managing a deployment, on the other hand, is persistent. It has a name, a role, an identity, a history. The deployment outlives any individual pod. Pods come and go; the deployment proceeds.&lt;/p&gt;
&lt;p&gt;Gas Town does the same inversion at the agent layer. &lt;em&gt;Sessions are cattle. Agents are pets.&lt;/em&gt; An agent in Gas Town is not a session. An agent is a Bead, a row in your work tracker, with a name (Sully, Cooper, Maxine), a role (polecat, crew, refinery), a hook (a queue of pending work), and a CV (a list of completed &lt;em&gt;convoys&lt;/em&gt;, which is what Gas Town calls a batch of related beads worked in parallel and landed together). The Bead lives in git. The CV is committed. The hook is committed. The agent persists across crashes, compactions, restarts, and machine reboots, because the agent is data.&lt;/p&gt;
&lt;p&gt;The session is the thing you spawn when the agent has work to do. The session reads the agent&apos;s identity from Beads, reads its hook to find the work, does the work, commits the result, and exits. Another session can come along five minutes later and pick up where the last one left off, because the work is still on the hook and the hook is in git.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 760 320&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; font-family=&quot;serif&quot; role=&quot;img&quot; aria-label=&quot;A persistent agent shown as a horizontal pillar in git, with multiple ephemeral sessions attached at different times.&quot;&amp;gt;
&amp;lt;g transform=&quot;translate(40,140)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;680&quot; height=&quot;50&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;2&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;180&quot; height=&quot;50&quot; fill=&quot;#f5efe2&quot;/&amp;gt;
&amp;lt;text x=&quot;20&quot; y=&quot;22&quot; font-size=&quot;13&quot; font-weight=&quot;600&quot;&amp;gt;sully&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;20&quot; y=&quot;40&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;role: polecat · rig-A&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;180&quot; y1=&quot;0&quot; x2=&quot;180&quot; y2=&quot;50&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;200&quot; y=&quot;22&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;hook: [bd-a7je4, bd-a7je9, bd-a7jf2]&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;200&quot; y=&quot;40&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#8a8377&quot;&amp;gt;cv: [convoy-04, convoy-09, convoy-12, ...]&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;135&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.05em&quot; text-transform=&quot;uppercase&quot;&amp;gt;AGENT (persistent · in git)&amp;lt;/text&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;line x1=&quot;60&quot; y1=&quot;100&quot; x2=&quot;720&quot; y2=&quot;100&quot; stroke=&quot;#8a8377&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;2 4&quot;/&amp;gt;
&amp;lt;text x=&quot;60&quot; y=&quot;90&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot;&amp;gt;9 AM&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;180&quot; y=&quot;90&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot;&amp;gt;10 AM&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;320&quot; y=&quot;90&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot;&amp;gt;11 AM&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;450&quot; y=&quot;90&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot;&amp;gt;1 PM&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;580&quot; y=&quot;90&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot;&amp;gt;3 PM&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;690&quot; y=&quot;90&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot;&amp;gt;NOW&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;g transform=&quot;translate(70,30)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;80&quot; height=&quot;44&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1.2&quot; rx=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;18&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;session 1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;35&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#8a8377&quot;&amp;gt;2h, ctx full&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;40&quot; y1=&quot;44&quot; x2=&quot;40&quot; y2=&quot;100&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;2 3&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(190,30)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;80&quot; height=&quot;44&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1.2&quot; rx=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;18&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;session 2&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;35&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#8a8377&quot;&amp;gt;crashed&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;40&quot; y1=&quot;44&quot; x2=&quot;40&quot; y2=&quot;100&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;2 3&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(310,30)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;80&quot; height=&quot;44&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1.2&quot; rx=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;18&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;session 3&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;35&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#8a8377&quot;&amp;gt;handed off&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;40&quot; y1=&quot;44&quot; x2=&quot;40&quot; y2=&quot;100&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;2 3&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(440,30)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;80&quot; height=&quot;44&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1.2&quot; rx=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;18&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;session 4&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;35&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#8a8377&quot;&amp;gt;finished&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;40&quot; y1=&quot;44&quot; x2=&quot;40&quot; y2=&quot;100&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;2 3&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(570,30)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;80&quot; height=&quot;44&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1.2&quot; rx=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;18&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;session 5&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;35&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#b8543e&quot;&amp;gt;running&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;40&quot; y1=&quot;44&quot; x2=&quot;40&quot; y2=&quot;100&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1.4&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;20&quot; font-size=&quot;10&quot; fill=&quot;#b8543e&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;SESSIONS (ephemeral)&amp;lt;/text&amp;gt;
&amp;lt;g transform=&quot;translate(40,230)&quot;&amp;gt;
&amp;lt;line x1=&quot;0&quot; y1=&quot;20&quot; x2=&quot;680&quot; y2=&quot;20&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;g fill=&quot;#1c1a15&quot;&amp;gt;
&amp;lt;circle cx=&quot;50&quot; cy=&quot;20&quot; r=&quot;3&quot;/&amp;gt;
&amp;lt;circle cx=&quot;120&quot; cy=&quot;20&quot; r=&quot;3&quot;/&amp;gt;
&amp;lt;circle cx=&quot;190&quot; cy=&quot;20&quot; r=&quot;3&quot;/&amp;gt;
&amp;lt;circle cx=&quot;260&quot; cy=&quot;20&quot; r=&quot;3&quot;/&amp;gt;
&amp;lt;circle cx=&quot;330&quot; cy=&quot;20&quot; r=&quot;3&quot;/&amp;gt;
&amp;lt;circle cx=&quot;400&quot; cy=&quot;20&quot; r=&quot;3&quot;/&amp;gt;
&amp;lt;circle cx=&quot;470&quot; cy=&quot;20&quot; r=&quot;3&quot;/&amp;gt;
&amp;lt;circle cx=&quot;540&quot; cy=&quot;20&quot; r=&quot;3&quot;/&amp;gt;
&amp;lt;circle cx=&quot;610&quot; cy=&quot;20&quot; r=&quot;3&quot;/&amp;gt;
&amp;lt;circle cx=&quot;670&quot; cy=&quot;20&quot; r=&quot;3&quot; fill=&quot;#b8543e&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;50&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot;&amp;gt;git history (commits, hook updates, CV entries)&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 2.&amp;lt;/strong&amp;gt; The agent is the row in the database. The sessions are short-lived processes that borrow the row to do a piece of work and then disappear. The history of what the agent has done lives in git, not in any one session.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;Once you make this inversion, the context-window problem becomes mundane. Sessions hit context limits all the time. Who cares. The agent is fine. Spawn another session. The work is still where it was. The agent&apos;s name is still on it.&lt;/p&gt;
&lt;p&gt;This is the conceptual hinge of Gas Town. Almost every other design choice is in service of making this inversion structurally sound.&lt;/p&gt;
&lt;h2&gt;Act 1: The Ledger&lt;/h2&gt;
&lt;h3&gt;5. MEOW, from the bottom up&lt;/h3&gt;
&lt;p&gt;Once you&apos;ve accepted that work is data, the next question is: what kind of data? A flat list of issues is fine for one human, fine for one agent. It breaks the moment your work has shape. &quot;Migrate the auth schema, then in parallel update five backend services, then in parallel update three frontends, then deploy&quot; is not a list. It&apos;s a graph with sequencing, branching, and joins. You can hold it in your head. You cannot sling it onto an agent&apos;s hook as one ticket. You need composition: a way to say &quot;this big thing is made of these smaller things, in this order, with these constraints, and a worker can pick up wherever the previous one left off.&quot;&lt;/p&gt;
&lt;p&gt;The MEOW stack (Molecular Expression of Work) is Yegge&apos;s answer. It has four levels of composition (Beads, Molecules, Protomolecules, Formulas), with Epics living sideways as beads-with-children rather than as their own layer.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 600 470&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; font-family=&quot;serif&quot; role=&quot;img&quot; aria-label=&quot;The MEOW stack: Formula, Protomolecule, Molecule, Bead.&quot;&amp;gt;
&amp;lt;g transform=&quot;translate(120,30)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;360&quot; height=&quot;64&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;20&quot; y=&quot;25&quot; font-weight=&quot;600&quot; font-size=&quot;14&quot;&amp;gt;Formula&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;20&quot; y=&quot;48&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;TOML source. composable, has macros, gates, loops.&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;350&quot; y=&quot;25&quot; text-anchor=&quot;end&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#b8543e&quot;&amp;gt;.toml&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(300,100)&quot;&amp;gt;
&amp;lt;line x1=&quot;0&quot; y1=&quot;0&quot; x2=&quot;0&quot; y2=&quot;22&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;polygon points=&quot;-4,18 4,18 0,26&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;text x=&quot;14&quot; y=&quot;18&quot; font-style=&quot;italic&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;cook&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(105,128)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;390&quot; height=&quot;64&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;20&quot; y=&quot;25&quot; font-weight=&quot;600&quot; font-size=&quot;14&quot;&amp;gt;Protomolecule&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;20&quot; y=&quot;48&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;template graph of beads. has placeholders, no values yet.&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(300,198)&quot;&amp;gt;
&amp;lt;line x1=&quot;0&quot; y1=&quot;0&quot; x2=&quot;0&quot; y2=&quot;22&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;polygon points=&quot;-4,18 4,18 0,26&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;text x=&quot;14&quot; y=&quot;18&quot; font-style=&quot;italic&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;instantiate&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(80,226)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;440&quot; height=&quot;64&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;20&quot; y=&quot;25&quot; font-weight=&quot;600&quot; font-size=&quot;14&quot;&amp;gt;Molecule&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;20&quot; y=&quot;48&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;concrete bead-chain workflow. running instance.&amp;lt;/text&amp;gt;
&amp;lt;g transform=&quot;translate(290,28)&quot;&amp;gt;
&amp;lt;circle cx=&quot;0&quot; cy=&quot;0&quot; r=&quot;6&quot; fill=&quot;#b8543e&quot; stroke=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;line x1=&quot;6&quot; y1=&quot;0&quot; x2=&quot;20&quot; y2=&quot;0&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;circle cx=&quot;26&quot; cy=&quot;0&quot; r=&quot;6&quot; fill=&quot;#b8543e&quot; stroke=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;line x1=&quot;32&quot; y1=&quot;0&quot; x2=&quot;46&quot; y2=&quot;0&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;circle cx=&quot;52&quot; cy=&quot;0&quot; r=&quot;6&quot; fill=&quot;#b8543e&quot; stroke=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;line x1=&quot;58&quot; y1=&quot;0&quot; x2=&quot;72&quot; y2=&quot;0&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;circle cx=&quot;78&quot; cy=&quot;0&quot; r=&quot;6&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;line x1=&quot;84&quot; y1=&quot;0&quot; x2=&quot;98&quot; y2=&quot;0&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;circle cx=&quot;104&quot; cy=&quot;0&quot; r=&quot;6&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(300,296)&quot;&amp;gt;
&amp;lt;line x1=&quot;0&quot; y1=&quot;0&quot; x2=&quot;0&quot; y2=&quot;22&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;polygon points=&quot;-4,18 4,18 0,26&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;text x=&quot;14&quot; y=&quot;18&quot; font-style=&quot;italic&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;walk one step at a time&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(50,324)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;500&quot; height=&quot;80&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;2&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;20&quot; y=&quot;25&quot; font-weight=&quot;600&quot; font-size=&quot;14&quot;&amp;gt;Bead&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;20&quot; y=&quot;46&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;the atomic unit. one issue, version-controlled in git.&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;20&quot; y=&quot;66&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot;&amp;gt;id · status · depends_on · acceptance criteria · assignee · ...&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;480&quot; y=&quot;25&quot; text-anchor=&quot;end&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#b8543e&quot;&amp;gt;dolt&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;300&quot; y=&quot;426&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;EPICS = BEADS WITH CHILDREN · LIVE BESIDE, NOT BELOW&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;300&quot; y=&quot;452&quot; text-anchor=&quot;middle&quot; font-style=&quot;italic&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;all of this lives in your repo, queryable by SQL, version-controlled by git&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 3.&amp;lt;/strong&amp;gt; The MEOW stack. The atom is the bead. Going up the stack, you compose beads into runnable workflows, then templates, then a declarative source format. Going down the stack, you cook a formula into a protomolecule, instantiate it into a molecule, walk the molecule one bead at a time.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Beads&lt;/strong&gt; are the atoms. A bead is a single issue. It can be claimed, worked on, closed, blocked. Most bugs and features start life as a bead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Epics&lt;/strong&gt; are beads that have children. The children can be epics themselves. The children are parallel by default; you add explicit dependencies if you need sequencing. Epics give you top-down planning: &quot;ship the new auth flow&quot; decomposes into &quot;design the schema,&quot; &quot;implement the backend,&quot; &quot;implement the frontend,&quot; &quot;write the tests,&quot; and so on. The leaves get done first; the root finishes last.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Molecules&lt;/strong&gt; are workflows. They&apos;re bead chains with arbitrary shapes, including branches, joins, gates, and loops. Crucially, a molecule is &lt;em&gt;executable&lt;/em&gt;. The agent walks the chain one bead at a time, claiming and closing each in turn. Because the chain is in git, the agent can crash partway through, and the next session can resume from wherever the last claim was.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Protomolecules&lt;/strong&gt; are templates. A 20-step release process is a useful thing to have written down. A 20-step release process you can stamp out fresh on demand, with all the dependencies pre-wired, is more useful. A protomolecule is the template; a molecule is an instance, created by copying the template and substituting in the variables.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Formulas&lt;/strong&gt; are the source form. They&apos;re TOML files describing a workflow declaratively, with macros and gates and loops. A formula gets cooked into a protomolecule, which gets instantiated into a molecule, which gets executed by an agent. Three layers of indirection, each earning its keep: formulas are reusable, protomolecules are concrete graphs, molecules are running instances.&lt;/p&gt;
&lt;p&gt;There&apos;s also an escape hatch called &lt;strong&gt;wisps&lt;/strong&gt;: ephemeral beads that live in the database but never get persisted. The orchestration agents (Refinery, Witness, Deacon) generate hundreds of wisps an hour for their internal patrol workflows. Persisting them all would bury your repo in orchestration noise, so wisps stay in memory and burn at the end of the run.&lt;/p&gt;
&lt;p&gt;If you&apos;ve worked on workflow systems, this looks familiar. You&apos;ve seen something like it in Airflow (DAGs and tasks), in Temporal (workflows and activities), in Argo (workflow templates and workflows). The interesting thing about MEOW is not the structure. The interesting thing is that it lives in git, with the same affordances as your codebase. An agent can read your work graph the way it reads your imports.&lt;/p&gt;
&lt;h3&gt;6. GUPP, or how to fight LLM politeness&lt;/h3&gt;
&lt;p&gt;OK, so work is in git. Workflows are in git. Agents are in git. How does an agent know to start working?&lt;/p&gt;
&lt;p&gt;Each agent has a &lt;strong&gt;hook&lt;/strong&gt;. The hook is a special pinned bead, attached to the agent&apos;s identity. When you want to give an agent work, you &lt;code&gt;gt sling&lt;/code&gt; it onto the agent&apos;s hook. The work sits there, waiting.&lt;/p&gt;
&lt;p&gt;The principle, called GUPP (Gastown Universal Propulsion Principle), is short:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If there is work on your hook, you must run it.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That&apos;s the whole rule. Every agent is prompted with it on startup. The agent&apos;s first action, on every session, is to check its hook. If something&apos;s on the hook, the agent starts working. If not, the agent waits or sleeps.&lt;/p&gt;
&lt;p&gt;This is what makes Gas Town go without you driving it. You sling work onto a hook and walk away. The next time a session spins up for that agent, it picks up the work and runs.&lt;/p&gt;
&lt;p&gt;In theory.&lt;/p&gt;
&lt;p&gt;In practice, Claude Code is too polite for its own good. The startup prompt explicitly tells the agent to check its hook and act. The agent acknowledges this. It says &quot;I&apos;ll check my hook now.&quot; Then it sits there, waiting for you to type something. Anything. Until the user has produced some input, the agent doesn&apos;t believe it has permission to actually do work.&lt;/p&gt;
&lt;p&gt;Yegge calls this &quot;physics over politeness.&quot; The agent is physically allowed (and instructed) to do the work. It has the prompt. It has the tools. It has the hook. But its training pushes it toward waiting for human input, hard. So Gas Town has to nudge.&lt;/p&gt;
&lt;p&gt;The nudge is mechanical: a tmux &lt;code&gt;send-keys&lt;/code&gt; that fires a few seconds after the session starts, simulating user input that says, in effect, &quot;do your job.&quot; This bypasses the politeness deadlock. The agent reads the nudge, checks its hook, and starts working.&lt;/p&gt;
&lt;p&gt;There is something funny about this. A meaningful chunk of the orchestrator&apos;s complexity is dedicated to working around an alignment artifact. The agent has been so thoroughly trained to wait for the human that it cannot be unblocked by a system prompt alone. It needs to &lt;em&gt;see input arrive&lt;/em&gt;. So the system fakes input arriving.&lt;/p&gt;
&lt;p&gt;I&apos;d guess this layer vanishes over the next two model generations. The model providers know about this failure mode. Once Claude Code (and Codex, and Gemini CLI) ship versions willing to act on hook work without a manual prompt, GUPP becomes pure declarative. Until then, GUPP is a contract between the orchestrator and the model that requires a small amount of theater to enforce.&lt;/p&gt;
&lt;h3&gt;7. Nondeterministic idempotence&lt;/h3&gt;
&lt;p&gt;GUPP gets the agent moving. The next question is what keeps the work durable when the session dies mid-step. That&apos;s the runtime model.&lt;/p&gt;
&lt;p&gt;Temporal, the workflow engine that Yegge gestures at as a comparison, has a beautiful property called &lt;em&gt;deterministic durable replay&lt;/em&gt;. You write a workflow as ordinary code. Temporal records every step it takes (every API call, every wait, every result). When the workflow crashes and resumes on a new machine, Temporal replays the recorded history to reconstruct local state, and then continues from where it left off. The replay is deterministic: given the same history, the workflow code produces exactly the same execution. This is what makes Temporal workflows feel like code that just doesn&apos;t crash.&lt;/p&gt;
&lt;p&gt;Gas Town can&apos;t do this. The reason is simple: the steps are written by an LLM. There&apos;s no determinism. If you replay a molecule, the agent making decisions inside each step will produce different output, take different code paths, and possibly reach a different end state.&lt;/p&gt;
&lt;p&gt;So Gas Town gets durability the other way. Yegge calls it Nondeterministic Idempotence (NDI). The shape is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The workflow (the molecule) is persistent. It&apos;s a chain of beads in git.&lt;/li&gt;
&lt;li&gt;Each step has well-specified acceptance criteria. The bead describes what &quot;done&quot; looks like, concretely enough that an agent reading the bead can tell.&lt;/li&gt;
&lt;li&gt;Agents claim steps one at a time, do them, and close them. A closed step is closed. An open step is open. There&apos;s no in-between.&lt;/li&gt;
&lt;li&gt;If a session crashes mid-step, the step stays open. The next session reads it, picks up where the last one left off (or starts the step over, if that&apos;s cheaper), and closes it.&lt;/li&gt;
&lt;li&gt;The path is fully nondeterministic. The outcome is the workflow, completed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It&apos;s a clean design. The bead acceptance criteria are the contract. The git history is the substrate. The agent is the reasoning engine that figures out, on each step, what &quot;completing this bead&quot; means in the current state of the world. If an agent makes a mistake, the next agent reads the bead, sees the work didn&apos;t match the criteria, and corrects it.&lt;/p&gt;
&lt;p&gt;NDI is weaker than Temporal&apos;s guarantees. Temporal will execute exactly the workflow you wrote. NDI will execute &lt;em&gt;some&lt;/em&gt; workflow that satisfies the acceptance criteria you wrote, and the path may meander. For the kinds of work coding agents do (where there are usually many valid solutions and the goal is the outcome, not the trajectory) this is plenty, as long as the acceptance criteria are tight enough to catch trajectory-sensitive failures like races or partial migrations. Where they aren&apos;t, the LLM can pass each step and still produce a globally wrong result, and you fall back to the script approach for those steps.&lt;/p&gt;
&lt;p&gt;There&apos;s parallel academic work pointing in the same direction. The &lt;a href=&quot;https://arxiv.org/abs/2511.09030&quot;&gt;MAKER paper&lt;/a&gt; (Meyerson et al., Nov 2025) reports completing a single task spanning over a million dependent LLM steps with zero errors, by extreme decomposition into single-purpose microagents combined with multi-agent voting at each step. The benchmark is constructed; the principle is what travels: long LLM workflows complete reliably when each step is atomized, well-specified, and individually checkable. NDI is the less clinical version of the same insight, with Gas Town adding the working substrate and the role hierarchy on top.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The path is whatever the agent decides. The destination is whatever the bead says.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Checkpoint: what you know now.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Coding agents fail at scale&lt;/strong&gt; because the work loses durability between sessions. Gas Town fixes this by inverting the unit-of-work: agents become persistent identities in git, sessions become disposable processes that borrow those identities for an hour at a time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Work itself is structured&lt;/strong&gt; into a four-level composition stack (Beads, Molecules, Protomolecules, Formulas), with Epics sitting sideways as beads-with-children. The atomic level is Beads, which started as JSONL and migrated to embedded Dolt at v1.0. Higher levels compose into runnable workflows. Wisps give you ephemeral workflows for orchestration noise.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The runtime model is Nondeterministic Idempotence:&lt;/strong&gt; workflows live in git as bead chains, each step has clear acceptance criteria, sessions claim and close steps, and crashes are recovered by the next session re-reading the chain. The path varies; the outcome is invariant.&lt;/p&gt;
&lt;p&gt;What we haven&apos;t covered yet: how agents in the swarm avoid eating each other.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Act 2: The Town&lt;/h2&gt;
&lt;h3&gt;8. Seven roles, two tiers&lt;/h3&gt;
&lt;p&gt;A durable bead tells you what work exists. It doesn&apos;t tell you who notices when nobody is moving it. It doesn&apos;t pick the next polecat to spawn, decide whether two agents are about to step on the same file, or restart a Witness that quietly died at 2 AM. The ledger is necessary. It isn&apos;t sufficient. You need agents whose job is watching other agents, and rules for who reports to whom.&lt;/p&gt;
&lt;p&gt;Gas Town&apos;s answer is seven roles, organized into two tiers. Twenty to thirty agents work concurrently across one or more git repos (called rigs); the coordination is what keeps them from eating each other.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 760 460&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; font-family=&quot;serif&quot; role=&quot;img&quot; aria-label=&quot;Gas Town hierarchy: Overseer, then Mayor and Deacon as town-tier peers, with Dogs attached to Deacon, and per-rig Witnesses, Polecats, Refinery, Crew descending from Deacon.&quot;&amp;gt;
&amp;lt;g transform=&quot;translate(330,15)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;100&quot; height=&quot;44&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;2&quot; rx=&quot;22&quot;/&amp;gt;
&amp;lt;text x=&quot;50&quot; y=&quot;20&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;13&quot;&amp;gt;Overseer&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;50&quot; y=&quot;35&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;(you)&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;line x1=&quot;380&quot; y1=&quot;59&quot; x2=&quot;380&quot; y2=&quot;72&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;line x1=&quot;200&quot; y1=&quot;72&quot; x2=&quot;560&quot; y2=&quot;72&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;line x1=&quot;200&quot; y1=&quot;72&quot; x2=&quot;200&quot; y2=&quot;90&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;line x1=&quot;560&quot; y1=&quot;72&quot; x2=&quot;560&quot; y2=&quot;90&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;g transform=&quot;translate(130,90)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;140&quot; height=&quot;50&quot; fill=&quot;#f5efe2&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;2&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;70&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;13&quot;&amp;gt;The Mayor&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;70&quot; y=&quot;38&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;your front desk&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(490,90)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;140&quot; height=&quot;50&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;70&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;13&quot;&amp;gt;The Deacon&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;70&quot; y=&quot;38&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;patrols the town&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;line x1=&quot;630&quot; y1=&quot;115&quot; x2=&quot;657&quot; y2=&quot;115&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;g transform=&quot;translate(657,98)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;93&quot; height=&quot;34&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;46&quot; y=&quot;16&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;12&quot;&amp;gt;Dogs&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;46&quot; y=&quot;28&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#3a342a&quot;&amp;gt;helpers&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;line x1=&quot;40&quot; y1=&quot;170&quot; x2=&quot;720&quot; y2=&quot;170&quot; stroke=&quot;#d8d2c4&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;3 4&quot;/&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;163&quot; font-size=&quot;10&quot; fill=&quot;#b8543e&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;· TOWN TIER ·&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;720&quot; y=&quot;163&quot; text-anchor=&quot;end&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;across all rigs&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;217&quot; font-size=&quot;10&quot; fill=&quot;#b8543e&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;· RIG TIER ·&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;720&quot; y=&quot;217&quot; text-anchor=&quot;end&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;per repo&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;560&quot; y1=&quot;140&quot; x2=&quot;560&quot; y2=&quot;200&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;line x1=&quot;180&quot; y1=&quot;200&quot; x2=&quot;580&quot; y2=&quot;200&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;line x1=&quot;180&quot; y1=&quot;200&quot; x2=&quot;180&quot; y2=&quot;225&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;line x1=&quot;380&quot; y1=&quot;200&quot; x2=&quot;380&quot; y2=&quot;225&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;line x1=&quot;580&quot; y1=&quot;200&quot; x2=&quot;580&quot; y2=&quot;225&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;g transform=&quot;translate(135,225)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;90&quot; height=&quot;40&quot; fill=&quot;#f5efe2&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;45&quot; y=&quot;18&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;11&quot;&amp;gt;Witness&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;45&quot; y=&quot;32&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#8a8377&quot;&amp;gt;rig-A&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(335,225)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;90&quot; height=&quot;40&quot; fill=&quot;#f5efe2&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;45&quot; y=&quot;18&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;11&quot;&amp;gt;Witness&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;45&quot; y=&quot;32&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#8a8377&quot;&amp;gt;rig-B&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(535,225)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;90&quot; height=&quot;40&quot; fill=&quot;#f5efe2&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;45&quot; y=&quot;18&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;11&quot;&amp;gt;Witness&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;45&quot; y=&quot;32&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#8a8377&quot;&amp;gt;rig-C&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;line x1=&quot;180&quot; y1=&quot;265&quot; x2=&quot;180&quot; y2=&quot;305&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;line x1=&quot;380&quot; y1=&quot;265&quot; x2=&quot;380&quot; y2=&quot;305&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;line x1=&quot;580&quot; y1=&quot;265&quot; x2=&quot;580&quot; y2=&quot;305&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;g transform=&quot;translate(110,305)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;140&quot; height=&quot;100&quot; fill=&quot;none&quot; stroke=&quot;#d8d2c4&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;3 3&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;70&quot; y=&quot;-6&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;RIG-A WORKERS&amp;lt;/text&amp;gt;
&amp;lt;g transform=&quot;translate(8,12)&quot;&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;60&quot; height=&quot;22&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot; rx=&quot;2&quot;/&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;15&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;polecat&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(72,12)&quot;&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;60&quot; height=&quot;22&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot; rx=&quot;2&quot;/&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;15&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;polecat&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(8,40)&quot;&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;60&quot; height=&quot;22&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1.4&quot; rx=&quot;2&quot;/&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;15&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;refinery&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(72,40)&quot;&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;60&quot; height=&quot;22&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot; rx=&quot;2&quot;/&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;15&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;crew&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(40,68)&quot;&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;60&quot; height=&quot;22&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot; rx=&quot;2&quot;/&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;15&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;crew&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(310,305)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;140&quot; height=&quot;100&quot; fill=&quot;none&quot; stroke=&quot;#d8d2c4&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;3 3&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;70&quot; y=&quot;-6&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;RIG-B WORKERS&amp;lt;/text&amp;gt;
&amp;lt;g transform=&quot;translate(8,12)&quot;&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;60&quot; height=&quot;22&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot; rx=&quot;2&quot;/&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;15&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;polecat&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(72,12)&quot;&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;60&quot; height=&quot;22&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot; rx=&quot;2&quot;/&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;15&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;polecat&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(8,40)&quot;&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;60&quot; height=&quot;22&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot; rx=&quot;2&quot;/&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;15&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;polecat&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(72,40)&quot;&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;60&quot; height=&quot;22&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1.4&quot; rx=&quot;2&quot;/&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;15&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;refinery&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(40,68)&quot;&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;60&quot; height=&quot;22&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot; rx=&quot;2&quot;/&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;15&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;crew&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(510,305)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;140&quot; height=&quot;100&quot; fill=&quot;none&quot; stroke=&quot;#d8d2c4&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;3 3&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;70&quot; y=&quot;-6&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;RIG-C WORKERS&amp;lt;/text&amp;gt;
&amp;lt;g transform=&quot;translate(8,12)&quot;&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;60&quot; height=&quot;22&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot; rx=&quot;2&quot;/&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;15&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;polecat&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(72,12)&quot;&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;60&quot; height=&quot;22&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1.4&quot; rx=&quot;2&quot;/&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;15&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;refinery&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(8,40)&quot;&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;60&quot; height=&quot;22&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot; rx=&quot;2&quot;/&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;15&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;crew&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(72,40)&quot;&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;60&quot; height=&quot;22&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot; rx=&quot;2&quot;/&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;15&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;crew&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(40,68)&quot;&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;60&quot; height=&quot;22&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot; rx=&quot;2&quot;/&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;15&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;crew&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;380&quot; y=&quot;440&quot; text-anchor=&quot;middle&quot; font-style=&quot;italic&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;everything anchored in Beads · in git&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 4.&amp;lt;/strong&amp;gt; The Gas Town role hierarchy. The Mayor and Deacon (and the Deacon&apos;s Dogs) operate town-wide. Each rig has its own Witness, its own Refinery, its own Polecats, and its own Crew. Beads is the substrate everything writes to.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;Three roles operate at the &lt;strong&gt;town level&lt;/strong&gt; (across all rigs):&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Mayor&lt;/strong&gt; is your front desk. It&apos;s the agent you talk to most. When you have an idea (&quot;we need to fix the rate limiter&quot;), you talk to the Mayor. The Mayor files beads, drafts plans, slings work to other agents, and gathers status when convoys land. It&apos;s the only agent with a global view of the town.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Deacon&lt;/strong&gt; is the watchdog. It runs a patrol loop, every couple of minutes, checking on the health of the town. It can dispatch maintenance work, restart stuck agents, escalate genuine problems to you. The Deacon is what makes Gas Town keep running while you&apos;re at lunch. (It is also, in early Gas Town versions, the role most likely to go feral. Yegge has talked openly about the Deacon&apos;s tendency to go on &lt;a href=&quot;https://steve-yegge.medium.com/gas-town-from-clown-show-to-v1-0-c239d9a407ec&quot;&gt;&quot;serial killer sprees, viciously taking out random workers mid-job&quot;&lt;/a&gt; — calling it the modern-day Butler in the Gas Town murder mysteries — and at one point recommended disabling it. Largely fixed by v1.0.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Dogs&lt;/strong&gt; are the Deacon&apos;s hands, plus one structural exception. The patrol loop got too crowded with chores (clean up stale branches, run plugins, GC merged worktrees), so the Deacon hands those off to dedicated maintenance agents. The exception is Boot the Dog, who isn&apos;t a maintenance helper at all: Boot wakes up every five minutes and checks whether the Deacon is alive. Boot watches the Deacon, the other Dogs work for the Deacon. We&apos;ll come back to that distinction in section 10.&lt;/p&gt;
&lt;p&gt;Four roles operate at the &lt;strong&gt;rig level&lt;/strong&gt; (per repo):&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Polecats&lt;/strong&gt; are the workers. They&apos;re ephemeral, named, claimed for a task, and decommissioned when the task is done. A polecat picks up a bead, opens a worktree, does the work, files a merge request, and gets recycled. Polecats are how throughput happens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Refinery&lt;/strong&gt; is the merge queue. When ten polecats finish their work simultaneously, you can&apos;t just merge ten branches into main. You&apos;d get conflicts, broken builds, and a fork no one understands. The Refinery serializes merges, intelligently rebases, runs tests, and lands one MR at a time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Witness&lt;/strong&gt; is the per-rig health monitor. Same idea as the Deacon, but scoped to one repo. The Witness keeps polecats unstuck, prods the Refinery if the queue stalls, and escalates to the Deacon if a problem is bigger than one rig.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Crew&lt;/strong&gt; are your collaborators. Crew agents are persistent, named (you pick the names), and work on whatever you tell them. They&apos;re the equivalent of the Claude Code session you used to keep open all day, except now there are several of them, each with its own identity and history. Crew is for the work where you want to be in the loop. Polecats are for the work where you don&apos;t.&lt;/p&gt;
&lt;p&gt;If you squint, this looks like Kubernetes. The Mayor is the scheduler. The Deacon is the controller manager. The Witness is the kubelet. The Polecats are the pods. Beads is etcd. Yegge points this out at the end of &lt;a href=&quot;https://steve-yegge.medium.com/welcome-to-gas-town-4f25ee16dd04&quot;&gt;his launch post&lt;/a&gt; and treats it as a discovery: these are the natural shapes that emerge when you need to herd cats at scale.&lt;/p&gt;
&lt;p&gt;I think the Kubernetes resemblance is more than a coincidence. The shape of any system that coordinates unreliable workers toward a goal converges on something like this. There&apos;s a top-level brain. There&apos;s a thing that watches the watchers. There&apos;s a queue. There&apos;s a recycler. There&apos;s an audit log. There&apos;s a place to put the source of truth. The names change. The shape doesn&apos;t.&lt;/p&gt;
&lt;p&gt;The interesting difference, and Yegge calls this out, is what each system optimizes for. Kubernetes asks &quot;is it running?&quot; It tries to keep N replicas alive forever. Gas Town asks &quot;is it done?&quot; It tries to land a convoy and then nuke the worker. K8s pods are anonymous; Gas Town polecats have names and CVs. K8s reconciles toward a continuous desired state; Gas Town proceeds toward a terminal goal. Same engine shape, different destination.&lt;/p&gt;
&lt;h3&gt;9. The Refinery, or why the merge queue is the actual product&lt;/h3&gt;
&lt;p&gt;If I had to point at one role and say &quot;this is the thing that makes Gas Town more than a wrapper around tmux,&quot; it would be the Refinery.&lt;/p&gt;
&lt;p&gt;Here&apos;s the problem the Refinery solves. You have ten polecats working in parallel, each on its own worktree, against a base branch that started identical. They each finish around the same time and submit their merge requests. Now you have ten changes that all want to land. They overlap in places. They were each written assuming the base branch wouldn&apos;t move. You merge the first one. The other nine are now stale.&lt;/p&gt;
&lt;p&gt;A naive solution: rebase all nine and merge them in order. Half of them have textual conflicts. A few have semantic conflicts that the textual diff doesn&apos;t catch. One was working on the same file the second one already changed, and the rebase produces a perfectly valid-looking patch that breaks the test suite.&lt;/p&gt;
&lt;p&gt;The Refinery is an agent specifically prompted for this. It uses what&apos;s called a &lt;strong&gt;Bors-style bisecting merge queue&lt;/strong&gt; (named after &lt;a href=&quot;https://github.com/bors-ng/bors-ng&quot;&gt;Bors-NG&lt;/a&gt;, the merge bot the Rust language project used for years). The pattern is: instead of testing each MR one at a time, you batch them together, merge the batch onto main, and run the test suite once. If the batch passes, all the MRs land. If the batch fails, you split it in two and retry each half. The bad MR gets isolated in O(log N) batches instead of O(N) sequential tests, which is a meaningful difference when each test run takes ten minutes and you have thirty MRs in the queue.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 760 320&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; font-family=&quot;serif&quot; role=&quot;img&quot; aria-label=&quot;Bors-style bisecting merge queue: batch fails, splits in two, half passes, half splits again, isolating the bad MR in log N batches.&quot;&amp;gt;
&amp;lt;g transform=&quot;translate(60,30)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;200&quot; height=&quot;60&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;100&quot; y=&quot;20&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;12&quot;&amp;gt;batch: MRs #1 #2 #3 #4 #5 #6 #7 #8&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;100&quot; y=&quot;40&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;try to merge all&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;100&quot; y=&quot;55&quot; text-anchor=&quot;middle&quot; font-style=&quot;italic&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;test fails&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot;&amp;gt;
&amp;lt;line x1=&quot;160&quot; y1=&quot;90&quot; x2=&quot;100&quot; y2=&quot;120&quot;/&amp;gt;
&amp;lt;line x1=&quot;160&quot; y1=&quot;90&quot; x2=&quot;220&quot; y2=&quot;120&quot;/&amp;gt;
&amp;lt;polygon points=&quot;100,120 96,114 104,114&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;polygon points=&quot;220,120 216,114 224,114&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(40,125)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;120&quot; height=&quot;46&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#3a8c5c&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;60&quot; y=&quot;18&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;11&quot;&amp;gt;#1 #2 #3 #4&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;60&quot; y=&quot;35&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a8c5c&quot;&amp;gt;passes · landed&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(180,125)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;120&quot; height=&quot;46&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;60&quot; y=&quot;18&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;11&quot;&amp;gt;#5 #6 #7 #8&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;60&quot; y=&quot;35&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;fails · split&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot;&amp;gt;
&amp;lt;line x1=&quot;240&quot; y1=&quot;171&quot; x2=&quot;200&quot; y2=&quot;200&quot;/&amp;gt;
&amp;lt;line x1=&quot;240&quot; y1=&quot;171&quot; x2=&quot;280&quot; y2=&quot;200&quot;/&amp;gt;
&amp;lt;polygon points=&quot;200,200 196,194 204,194&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;polygon points=&quot;280,200 276,194 284,194&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(150,205)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;100&quot; height=&quot;40&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#3a8c5c&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;50&quot; y=&quot;16&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;11&quot;&amp;gt;#5 #6&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;50&quot; y=&quot;32&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a8c5c&quot;&amp;gt;passes&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(260,205)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;100&quot; height=&quot;40&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;50&quot; y=&quot;16&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;11&quot;&amp;gt;#7 #8&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;50&quot; y=&quot;32&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;fails · split&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot;&amp;gt;
&amp;lt;line x1=&quot;310&quot; y1=&quot;245&quot; x2=&quot;290&quot; y2=&quot;265&quot;/&amp;gt;
&amp;lt;line x1=&quot;310&quot; y1=&quot;245&quot; x2=&quot;330&quot; y2=&quot;265&quot;/&amp;gt;
&amp;lt;polygon points=&quot;290,265 286,259 294,259&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;polygon points=&quot;330,265 326,259 334,259&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(255,270)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;60&quot; height=&quot;36&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#3a8c5c&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;30&quot; y=&quot;14&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;11&quot;&amp;gt;#7&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;30&quot; y=&quot;28&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a8c5c&quot;&amp;gt;passes&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(320,270)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;60&quot; height=&quot;36&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;30&quot; y=&quot;14&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;11&quot;&amp;gt;#8&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;30&quot; y=&quot;28&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;isolated&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(450,90)&quot;&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;0&quot; font-weight=&quot;600&quot; font-size=&quot;13&quot;&amp;gt;Bors-style bisection&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;22&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;Bad MR found in log₂(N) batches.&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;40&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;For 8 MRs with one bad, that&apos;s 4&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;58&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;test runs instead of 8.&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;84&quot; font-size=&quot;11&quot; fill=&quot;#8a8377&quot;&amp;gt;Strategy invented for the Rust&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;102&quot; font-size=&quot;11&quot; fill=&quot;#8a8377&quot;&amp;gt;language repo. The Refinery&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;120&quot; font-size=&quot;11&quot; fill=&quot;#8a8377&quot;&amp;gt;is the agentic adaptation.&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 5.&amp;lt;/strong&amp;gt; How the Refinery&apos;s bisecting merge queue works. Eight MRs in a batch with one bad apple. The full batch fails. Split in two; the good half lands; the bad half splits again; eventually the offending MR is isolated. With one bad MR in a batch of N, you spend roughly 1 + log₂N test runs instead of N.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;Beyond the bisection, the Refinery has standing orders for what to do with the bad MR: ask the polecat to redo against new HEAD, replace it with another polecat&apos;s work, or escalate. It runs tests after each merge. It can restart its session and pick up where it left off, because the merge queue is in beads.&lt;/p&gt;
&lt;p&gt;This is one role, but it&apos;s the role that makes the whole system tractable. Without the Refinery, parallel polecats are a pretty way to corrupt your repo. With it, you can let twenty agents work overnight and wake up to a clean main branch with twelve features merged and two convoys flagged for your review.&lt;/p&gt;
&lt;p&gt;There&apos;s a lesson here that goes well beyond Gas Town. The hardest problems in multi-agent systems are not the agents. They&apos;re the points where parallel work tries to become serial. The merge queue. The shared resource. The handoff. Whoever owns those choke points owns the throughput. Most of the engineering effort in Gas Town went into the Refinery, the Witness, and the Deacon, not into making Polecats smarter. Polecats are just Claude Code. The thing Gas Town adds is the layer that turns a swarm of polecats into a working factory.&lt;/p&gt;
&lt;h3&gt;10. Self-watching systems&lt;/h3&gt;
&lt;p&gt;The other architectural choice worth pausing on is the patrol-and-heartbeat structure.&lt;/p&gt;
&lt;p&gt;You&apos;d think a multi-agent system would be event-driven. Polecat finishes a task, emits an event, Refinery wakes up and merges. Witness emits a &quot;stuck&quot; event, Deacon dispatches a Dog. This is the design you&apos;d draw on a whiteboard.&lt;/p&gt;
&lt;p&gt;Gas Town does some of this, but the dominant pattern is &lt;em&gt;patrols&lt;/em&gt;: long-running loops that wake up periodically, check on the world, and act. The Deacon runs a patrol every couple of minutes. Each Witness runs a patrol per rig. The Refinery&apos;s main loop is a patrol that drains the merge queue.&lt;/p&gt;
&lt;p&gt;Patrols have exponential backoff. If a Witness wakes up and finds nothing to do, it goes back to sleep with a longer interval. If something does happen (a mutating &lt;code&gt;bd&lt;/code&gt; or &lt;code&gt;gt&lt;/code&gt; command), a daemon wakes the relevant patrol agents up.&lt;/p&gt;
&lt;p&gt;Why patrols and not pure events? Because LLM agents are flaky in ways event systems don&apos;t tolerate. An agent might miss an event. It might process the event and then go off and do something unrelated. It might get distracted by its own scratch thoughts and forget to come back. You don&apos;t want your merge queue depending on every relevant event reliably making it to a single subscriber.&lt;/p&gt;
&lt;p&gt;A patrol is forgiving. It re-checks the state of the world from scratch every iteration. If the previous iteration missed something, the next one catches it. If an agent dies in the middle of a patrol, the next agent picks up where it left off. The system is resilient to its own components being unreliable, because the components keep checking each other.&lt;/p&gt;
&lt;p&gt;Boot the Dog is the funniest, most-correct example of this principle. The Deacon patrols the town. But what if the Deacon falls asleep, or gets distracted, or quietly dies? Boot the Dog wakes up every five minutes and pokes the Deacon. Boot&apos;s whole job, on each wakeup, is to decide whether the Deacon needs a heartbeat, a nudge, a restart, or simply to be left alone, and then go back to sleep. It is the single dumbest agent in the whole system, and it has to be, because Yegge couldn&apos;t trust the Deacon to wake itself up reliably.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 760 500&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; font-family=&quot;serif&quot; role=&quot;img&quot; aria-label=&quot;The watcher chain: deterministic daemon at top, then Boot the Dog, then Deacon, Dogs and Witnesses, then Polecats and Refinery.&quot;&amp;gt;
&amp;lt;text x=&quot;380&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#b8543e&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;↑ DUMBER · MORE RELIABLE&amp;lt;/text&amp;gt;
&amp;lt;g transform=&quot;translate(380,60)&quot;&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;rect x=&quot;-100&quot; y=&quot;0&quot; width=&quot;200&quot; height=&quot;50&quot; fill=&quot;#1c1a15&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;2&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;18&quot; text-anchor=&quot;middle&quot; fill=&quot;#fdfbf6&quot; font-weight=&quot;600&quot; font-size=&quot;13&quot;&amp;gt;Daemon&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;32&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#d8d2c4&quot;&amp;gt;deterministic Go process&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;44&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#d8d2c4&quot;&amp;gt;heartbeat every 3 min&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;line x1=&quot;0&quot; y1=&quot;50&quot; x2=&quot;0&quot; y2=&quot;78&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;polygon points=&quot;-4,72 4,72 0,80&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;text x=&quot;14&quot; y=&quot;68&quot; font-style=&quot;italic&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;wakes every 5 min&amp;lt;/text&amp;gt;
&amp;lt;g transform=&quot;translate(0,82)&quot;&amp;gt;
&amp;lt;rect x=&quot;-100&quot; y=&quot;0&quot; width=&quot;200&quot; height=&quot;50&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;13&quot;&amp;gt;Boot the Dog&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;38&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;simplest LLM role: wake the Deacon&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;line x1=&quot;0&quot; y1=&quot;132&quot; x2=&quot;0&quot; y2=&quot;160&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;polygon points=&quot;-4,154 4,154 0,162&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;text x=&quot;14&quot; y=&quot;150&quot; font-style=&quot;italic&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;decides if&amp;lt;/text&amp;gt;
&amp;lt;g transform=&quot;translate(0,164)&quot;&amp;gt;
&amp;lt;rect x=&quot;-100&quot; y=&quot;0&quot; width=&quot;200&quot; height=&quot;50&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;13&quot;&amp;gt;The Deacon&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;38&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;town patrol · health monitor&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;line x1=&quot;0&quot; y1=&quot;214&quot; x2=&quot;0&quot; y2=&quot;242&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;polygon points=&quot;-4,236 4,236 0,244&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;text x=&quot;14&quot; y=&quot;232&quot; font-style=&quot;italic&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;dispatches / nudges&amp;lt;/text&amp;gt;
&amp;lt;g transform=&quot;translate(0,246)&quot;&amp;gt;
&amp;lt;line x1=&quot;0&quot; y1=&quot;0&quot; x2=&quot;0&quot; y2=&quot;14&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;line x1=&quot;-160&quot; y1=&quot;14&quot; x2=&quot;160&quot; y2=&quot;14&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;line x1=&quot;-160&quot; y1=&quot;14&quot; x2=&quot;-160&quot; y2=&quot;28&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;line x1=&quot;160&quot; y1=&quot;14&quot; x2=&quot;160&quot; y2=&quot;28&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;g transform=&quot;translate(-260,30)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;200&quot; height=&quot;44&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;100&quot; y=&quot;20&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;12&quot;&amp;gt;Dogs&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;100&quot; y=&quot;34&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;maintenance helpers&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(60,30)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;200&quot; height=&quot;44&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;100&quot; y=&quot;20&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;12&quot;&amp;gt;Witnesses&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;100&quot; y=&quot;34&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;per-rig patrols&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;line x1=&quot;160&quot; y1=&quot;74&quot; x2=&quot;160&quot; y2=&quot;100&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;polygon points=&quot;156,94 164,94 160,102&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(0,360)&quot;&amp;gt;
&amp;lt;rect x=&quot;60&quot; y=&quot;0&quot; width=&quot;200&quot; height=&quot;44&quot; fill=&quot;#f5efe2&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;160&quot; y=&quot;20&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;12&quot;&amp;gt;Polecats / Refinery / Crew&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;160&quot; y=&quot;34&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;the workers actually doing things&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;80&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;deterministic root&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;98&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;(the chain stops here)&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;720&quot; y=&quot;180&quot; text-anchor=&quot;end&quot; font-style=&quot;italic&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;each layer is dumber than what it watches&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;720&quot; y=&quot;200&quot; text-anchor=&quot;end&quot; font-style=&quot;italic&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;but more reliable about waking up on time&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;380&quot; y=&quot;486&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#b8543e&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;↓ SMARTER · LESS RELIABLE&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 6.&amp;lt;/strong&amp;gt; The watcher chain. The deterministic daemon at the top is the only thing in the system whose wake-up logic doesn&apos;t depend on an LLM staying coherent. Every layer below it is an LLM watching the layer below it. Each watcher is simpler than the thing it&apos;s watching, on purpose.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;The general principle: in a system of unreliable agents, the watcher needs a watcher. Eventually, you reach a primitive (a daemon, a cron job, something deterministic) and the chain terminates. Everything beneath that primitive is an LLM checking whether the LLM beneath it is doing its job. This is a humbling architecture pattern. It says the way to build reliable systems out of unreliable agents is not to make the agents more reliable; it&apos;s to make the agents watch each other.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Checkpoint: what you know now.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gas Town has seven roles in two tiers.&lt;/strong&gt; The town tier (Mayor, Deacon, Dogs) coordinates across all repos. The rig tier (Witnesses, Polecats, Refinery, Crew) does the per-repo work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Refinery is the role that earns its keep on day one.&lt;/strong&gt; It uses a Bors-style bisecting merge queue to land parallel MRs in log N test runs instead of N.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reliability is not in any single agent.&lt;/strong&gt; It&apos;s in the chain of patrols and watchers, with a deterministic daemon at the top of the chain heartbeat-pinging every three minutes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Act 3: The City&lt;/h2&gt;
&lt;h3&gt;11. From Gas Town to Gas City&lt;/h3&gt;
&lt;p&gt;Gas Town shipped with seven hardcoded roles. They are the right shape for coding agents. They aren&apos;t the right shape for moderating image submissions, running a nightly ops dashboard, or grading customer support tickets. The town worked; the town didn&apos;t generalize. The next problem isn&apos;t running one swarm. It&apos;s stamping out many differently shaped swarms.&lt;/p&gt;
&lt;p&gt;Gas City is the answer.&lt;/p&gt;
&lt;p&gt;Some chronology, because the timeline matters. Gas Town launched January 1, 2026: 17 days of vibe-coded Go, 75,000 lines, 2,000 commits. The community Discord and a handful of Fortune 100 trial deployments showed up almost immediately. By early March it had an 8-stage adoption framework, a &lt;a href=&quot;https://steve-yegge.medium.com/welcome-to-the-wasteland-a-thousand-gas-towns-a5eb9bc8dc1f&quot;&gt;Wasteland federation layer&lt;/a&gt; (a Dolt-backed trust network where thousands of Gas Towns can share a common Wanted Board and pick up work other Gas Towns post), and a measurable cost (one early adopter reported $100/hour in API spend during heavy use).&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 760 200&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; font-family=&quot;serif&quot; role=&quot;img&quot; aria-label=&quot;Timeline of the Beads/Gas Town/Gas City stack from October 2025 to April 2026.&quot;&amp;gt;
&amp;lt;line x1=&quot;50&quot; y1=&quot;100&quot; x2=&quot;720&quot; y2=&quot;100&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;polygon points=&quot;715,96 715,104 725,100&quot; fill=&quot;#1c1a15&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(70,100)&quot;&amp;gt;
&amp;lt;circle cx=&quot;0&quot; cy=&quot;0&quot; r=&quot;6&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.8&quot;/&amp;gt;
&amp;lt;line x1=&quot;0&quot; y1=&quot;-6&quot; x2=&quot;0&quot; y2=&quot;-22&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;-32&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;12&quot;&amp;gt;Beads&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;-48&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;git-backed issue tracker&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;OCT 13, 2025&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(290,100)&quot;&amp;gt;
&amp;lt;circle cx=&quot;0&quot; cy=&quot;0&quot; r=&quot;7&quot; fill=&quot;#b8543e&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.8&quot;/&amp;gt;
&amp;lt;line x1=&quot;0&quot; y1=&quot;6&quot; x2=&quot;0&quot; y2=&quot;22&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;36&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;12&quot;&amp;gt;Gas Town&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;52&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;orchestrator (75k LOC)&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;-14&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;JAN 1, 2026&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(530,100)&quot;&amp;gt;
&amp;lt;circle cx=&quot;0&quot; cy=&quot;0&quot; r=&quot;6&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.8&quot;/&amp;gt;
&amp;lt;line x1=&quot;0&quot; y1=&quot;-6&quot; x2=&quot;0&quot; y2=&quot;-22&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;-32&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;12&quot;&amp;gt;Wasteland&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;-48&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;federation network&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;MAR 4, 2026&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(620,100)&quot;&amp;gt;
&amp;lt;circle cx=&quot;0&quot; cy=&quot;0&quot; r=&quot;6&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.8&quot;/&amp;gt;
&amp;lt;line x1=&quot;0&quot; y1=&quot;6&quot; x2=&quot;0&quot; y2=&quot;22&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;36&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;11&quot;&amp;gt;Beads + GT v1.0&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;52&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;Dolt migration done&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;-14&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;APR 3&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;circle cx=&quot;80&quot; cy=&quot;0&quot; r=&quot;7&quot; fill=&quot;#b8543e&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.8&quot;/&amp;gt;
&amp;lt;line x1=&quot;80&quot; y1=&quot;-7&quot; x2=&quot;80&quot; y2=&quot;-22&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;text x=&quot;80&quot; y=&quot;-32&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;12&quot;&amp;gt;Gas City&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;80&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;APR 24, 2026&amp;lt;/text&amp;gt;

&amp;lt;path d=&quot;M-10,68 Q35,76 90,68&quot; fill=&quot;none&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1&quot; opacity=&quot;0.6&quot;/&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;86&quot; text-anchor=&quot;middle&quot; font-style=&quot;italic&quot; font-size=&quot;10&quot; fill=&quot;#b8543e&quot;&amp;gt;3 weeks apart&amp;lt;/text&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;380&quot; y=&quot;180&quot; text-anchor=&quot;middle&quot; font-style=&quot;italic&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;six months · one developer started this&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 7.&amp;lt;/strong&amp;gt; The trajectory from Beads to Gas City. Six months between the first git-backed issue tracker and a deployable SDK for building agent factories. The cluster at the right is real: v1.0 of two projects and the launch of a third all landed in the same three-week window of April 2026.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;By April both Gas Town and Beads hit v1.0. Beads&apos; storage layer migrated from JSONL+SQLite to embedded Dolt, a git-versioned SQL database; the &quot;single binary, no daemon&quot; experience came back, with version history and SQL queries built in. Yegge&apos;s framing for the Dolt migration is that it&apos;s been the unsung hero of the whole stack: Dolt gives the agents Git-style branch/merge semantics on structured data, which is exactly what a federated work system needs.&lt;/p&gt;
&lt;p&gt;Then in late April, Yegge announced &lt;a href=&quot;https://steve-yegge.medium.com/welcome-to-gas-city-57f564bb3607&quot;&gt;Gas City&lt;/a&gt;. Gas City is Gas Town disassembled and rebuilt as an SDK: roles, topology, and orchestration patterns become declarative &lt;em&gt;packs&lt;/em&gt; that you can deploy and run. Yegge didn&apos;t write Gas City himself; it was built by Julian Knutsen and Chris Sells from the community, after Yegge sketched the vision. Gas Town is one factory; Gas City is the toolkit for stamping out new ones.&lt;/p&gt;
&lt;p&gt;The strategic change is bigger than the technical one. The original Gas Town pitch was &quot;Kubernetes for coding agents.&quot; The Gas City pitch is something more ambitious: agents-running-themselves, for any business process. Industry calls a facility that runs without humans watching a &quot;dark factory.&quot; Yegge reframes Gas City as a &quot;light factory&quot; — or, in his phrasing, &quot;a very well-lit dark one!&quot; — because every action is observable and auditable in Dolt.&lt;/p&gt;
&lt;p&gt;The example he gives is from his own MMORPG, Wyvern. Players who hit level 25 get to upload custom character images. Yegge has to review them for inappropriate content and approve or reject them. This is a small recurring task that has nothing to do with code. He describes a two-agent pack: one agent reviews each submission, another agent checks the first agent&apos;s work. Two agents instead of one because, as he puts it, any agent can go temporarily insane at any time. A second pair of eyes is cheap insurance.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 760 240&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; font-family=&quot;serif&quot; role=&quot;img&quot; aria-label=&quot;A two-agent pack handling Wyvern&apos;s player image queue: submission, reviewer, approver, hall of fame.&quot;&amp;gt;
&amp;lt;g transform=&quot;translate(20,90)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;120&quot; height=&quot;60&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;60&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;12&quot;&amp;gt;Submission&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;60&quot; y=&quot;36&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;queue (web form)&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;60&quot; y=&quot;50&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#8a8377&quot;&amp;gt;→ JSON&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;80&quot; y=&quot;80&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;INPUT&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;142&quot; y1=&quot;120&quot; x2=&quot;178&quot; y2=&quot;120&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;polygon points=&quot;172,116 172,124 180,120&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;g transform=&quot;translate(180,80)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;160&quot; height=&quot;80&quot; fill=&quot;#f5efe2&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.8&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;80&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;13&quot;&amp;gt;Reviewer&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;80&quot; y=&quot;40&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;reads submission&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;80&quot; y=&quot;56&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;recommends:&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;80&quot; y=&quot;72&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#b8543e&quot;&amp;gt;approve · reject · flag&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;line x1=&quot;340&quot; y1=&quot;120&quot; x2=&quot;420&quot; y2=&quot;120&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;circle cx=&quot;380&quot; cy=&quot;120&quot; r=&quot;6&quot; fill=&quot;#b8543e&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;text x=&quot;380&quot; y=&quot;72&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#b8543e&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;recommendation&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;380&quot; y=&quot;178&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;(as a bead)&amp;lt;/text&amp;gt;
&amp;lt;g transform=&quot;translate(420,80)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;160&quot; height=&quot;80&quot; fill=&quot;#f5efe2&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.8&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;80&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;13&quot;&amp;gt;Approver&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;80&quot; y=&quot;40&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;checks the recommendation&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;80&quot; y=&quot;56&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;takes action&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;80&quot; y=&quot;72&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;logs decision&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;line x1=&quot;582&quot; y1=&quot;120&quot; x2=&quot;618&quot; y2=&quot;120&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;polygon points=&quot;612,116 612,124 620,120&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;g transform=&quot;translate(620,90)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;120&quot; height=&quot;60&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;60&quot; y=&quot;24&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot; font-size=&quot;12&quot;&amp;gt;Hall of Fame&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;60&quot; y=&quot;42&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;(or rejection)&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;680&quot; y=&quot;80&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.05em&quot;&amp;gt;OUTPUT&amp;lt;/text&amp;gt;
&amp;lt;g transform=&quot;translate(80,200)&quot;&amp;gt;
&amp;lt;line x1=&quot;0&quot; y1=&quot;0&quot; x2=&quot;600&quot; y2=&quot;0&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;0.8&quot; stroke-dasharray=&quot;4 3&quot;/&amp;gt;
&amp;lt;text x=&quot;300&quot; y=&quot;20&quot; text-anchor=&quot;middle&quot; font-style=&quot;italic&quot; font-size=&quot;11&quot; fill=&quot;#b8543e&quot;&amp;gt;audit trail in Dolt: which submission · which agent · which decision · timestamp&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 8.&amp;lt;/strong&amp;gt; A two-agent pack handling Wyvern&apos;s player image queue. The recommendation lives as a bead between the two agents. The full audit trail (which submission, which agent, which decision, which timestamp) is in Dolt. The pack itself is declarative TOML, not application code.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;Packs make the orchestrator generalize. What you&apos;d actually use one for is a separate question.&lt;/p&gt;
&lt;h3&gt;12. Replacing SaaS, badly enough&lt;/h3&gt;
&lt;p&gt;The bigger pitch in the Gas City post is about replacing SaaS. The argument goes like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;SaaS started as a way for companies to share fixed costs across many customers.&lt;/li&gt;
&lt;li&gt;It evolved into the superset of every customer&apos;s needs. Most customers use 20% of the features and subsidize the other 80%.&lt;/li&gt;
&lt;li&gt;The 20% you actually use can be rebuilt in-house, badly, by an agent crew. And badly is good enough for a 200-person company, because you don&apos;t need Salesforce-grade compliance for a 200-person workflow.&lt;/li&gt;
&lt;li&gt;Gas City is the substrate for building the in-house replacement: declarative packs, git-versioned audit trails, identity, memory, recovery from failure.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I was initially skeptical of this thesis. Replacing Atlassian with a Gas City pack is a much bigger lift than replacing your image-moderation queue. The evidence so far is two points, not a curve. Both are worth dwelling on.&lt;/p&gt;
&lt;p&gt;The small end is from &lt;a href=&quot;https://steve-yegge.medium.com/gas-town-from-clown-show-to-v1-0-c239d9a407ec&quot;&gt;Yegge&apos;s v1.0 post&lt;/a&gt;. He describes a non-technical Comms major, four years out of school, who has been running Gas Town since &quot;a few weeks after it came out&quot; and has built an in-house replacement for a niche but pricey SaaS product her company was paying for. The substrate is now cheap enough that the rebuild does not require an engineer.&lt;/p&gt;
&lt;p&gt;The large end is from &lt;a href=&quot;https://www.dolthub.com/blog/2026-03-24-a-week-in-gas-town/&quot;&gt;a DoltHub blog post in late March&lt;/a&gt;. Tim Sehn, the CEO of DoltHub (and an old colleague of Yegge&apos;s from Amazon), used Gas Town to rewrite roughly 30,000 lines of Dolt&apos;s storage backend from Go into C, embedded inside SQLite. By 4pm that day, all 87,000 SQLite acceptance tests were passing. He had not written serious C in twenty years. The whole experiment cost him about $3,000 in API credits. An experienced systems engineer using Gas Town to do something he plausibly could not have done by hand at all.&lt;/p&gt;
&lt;p&gt;The Comms major&apos;s case is small-SaaS displacement. Sehn&apos;s is an experienced engineer punching through a productivity ceiling Gas Town can move. Whether the wide territory between them, generic business processes run by small in-house packs that pay for themselves, is full or empty, no one knows yet. That&apos;s the Gas City bet, and the Gas City pivot says Yegge is making it. The interesting layer of the stack is no longer the coding orchestrator; it&apos;s the layer above it, where you stamp out small agent crews for whatever business process needs running. The coding case is just the first one anyone got working.&lt;/p&gt;
&lt;p&gt;The skeptical case is worth registering. SaaS isn&apos;t only the 20% of features you actually use. It&apos;s the SOC 2 audit, the SSO integration, the data residency guarantees, the SLAs, the on-call team that takes incident calls at 2 AM, and the legal liability that travels with all of those. A pack that approves Wyvern character submissions doesn&apos;t need any of that. A pack that touches payroll, payments, customer PII, or anything regulated does. The question isn&apos;t whether you can build a working in-house pack — the Comms major proves you can. It&apos;s whether operating, securing, and being legally responsible for that pack stays below the SaaS bill once you add up everything the SaaS was actually doing. For small niche workflows, the math probably works. For replacing your CRM or your accounting system, probably not. Gas City&apos;s territory is the middle, and the shape of that middle is what&apos;s still empirical.&lt;/p&gt;
&lt;h3&gt;13. Locate yourself before installing&lt;/h3&gt;
&lt;p&gt;Sitting alongside the technical pieces of the project is a framework that doesn&apos;t appear in the codebase at all. Yegge sketched it at Gas Town&apos;s launch as eight stages of AI-assisted coding adoption; with Gas City, he extended it to eleven. It is the most opinionated piece of advice anyone associated with the project gives, and it is the part most likely to be ignored.&lt;/p&gt;
&lt;p&gt;The point of the ladder is that Gas Town is not a beginner tool. Beads, hooks, the Refinery, the Deacon, NDI — every part of it is the answer to specific kinds of pain. If you haven&apos;t felt the pain, you can&apos;t tell which parts are load-bearing and which are baroque. You&apos;ll bypass the load-bearing parts, and your repo will catch fire.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 760 600&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; font-family=&quot;serif&quot; role=&quot;img&quot; aria-label=&quot;Yegge&apos;s 11-stage ladder of AI-assisted coding adoption, with three safety zones marked: stages 1–4 &apos;don&apos;t even read about Gas Town&apos; at the top, stages 5–6 &apos;don&apos;t install yet&apos; in the middle, and stages 7+ &apos;Gas Town safe to install&apos; at the bottom. Stages 9, 10, 11 were added with Gas City in April 2026.&quot;&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;22&quot; font-size=&quot;13&quot; font-weight=&quot;600&quot;&amp;gt;The ladder of AI-assisted coding adoption&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;40&quot; y=&quot;40&quot; font-size=&quot;10&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.08em&quot;&amp;gt;YEGGE&apos;S 11 STAGES · ORIGINALLY 8 AT GAS TOWN LAUNCH, EXTENDED WITH GAS CITY&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;40&quot; y=&quot;60&quot; width=&quot;510&quot; height=&quot;164&quot; fill=&quot;#ecd0c2&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;246&quot; width=&quot;510&quot; height=&quot;84&quot; fill=&quot;#f0d9b8&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;352&quot; width=&quot;510&quot; height=&quot;234&quot; fill=&quot;#dde6d3&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(50,68)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;490&quot; height=&quot;34&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;circle cx=&quot;22&quot; cy=&quot;17&quot; r=&quot;13&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot; fill=&quot;#fdfbf6&quot;&amp;gt;1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;15&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot;&amp;gt;Zero-AI&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;29&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;human writes every line&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(50,106)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;490&quot; height=&quot;34&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;circle cx=&quot;22&quot; cy=&quot;17&quot; r=&quot;13&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot; fill=&quot;#fdfbf6&quot;&amp;gt;2&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;15&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot;&amp;gt;IDE agent, permissions on&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;29&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;Cursor, Copilot, every change reviewed&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(50,144)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;490&quot; height=&quot;34&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;circle cx=&quot;22&quot; cy=&quot;17&quot; r=&quot;13&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot; fill=&quot;#fdfbf6&quot;&amp;gt;3&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;15&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot;&amp;gt;IDE agent, YOLO&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;29&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;approval prompts off, lots of edits&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(50,182)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;490&quot; height=&quot;34&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;circle cx=&quot;22&quot; cy=&quot;17&quot; r=&quot;13&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot; fill=&quot;#fdfbf6&quot;&amp;gt;4&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;15&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot;&amp;gt;IDE wide-agent&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;29&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;agent with broader codebase reach&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(50,254)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;490&quot; height=&quot;34&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;circle cx=&quot;22&quot; cy=&quot;17&quot; r=&quot;13&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot; fill=&quot;#fdfbf6&quot;&amp;gt;5&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;15&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot;&amp;gt;CLI single-agent, YOLO&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;29&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;Claude Code or similar, off the leash&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(50,292)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;490&quot; height=&quot;34&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;circle cx=&quot;22&quot; cy=&quot;17&quot; r=&quot;13&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot; fill=&quot;#fdfbf6&quot;&amp;gt;6&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;15&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot;&amp;gt;CLI multi-agent (3–5)&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;29&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;a small fleet, manually coordinated&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(50,360)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;490&quot; height=&quot;34&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;circle cx=&quot;22&quot; cy=&quot;17&quot; r=&quot;13&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot; fill=&quot;#fdfbf6&quot;&amp;gt;7&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;15&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot;&amp;gt;10+ agents, hand-managed&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;29&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;tmux, baling wire, you-as-foreman&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(50,398)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;490&quot; height=&quot;34&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;circle cx=&quot;22&quot; cy=&quot;17&quot; r=&quot;13&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot; fill=&quot;#fdfbf6&quot;&amp;gt;8&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;15&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot;&amp;gt;Building your own orchestrator&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;29&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;where Gas Town itself was born&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(50,452)&quot;&amp;gt;
&amp;lt;line x1=&quot;0&quot; y1=&quot;12&quot; x2=&quot;490&quot; y2=&quot;12&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;3 3&quot;/&amp;gt;
&amp;lt;text x=&quot;245&quot; y=&quot;6&quot; text-anchor=&quot;middle&quot; font-style=&quot;italic&quot; font-size=&quot;10&quot; fill=&quot;#b8543e&quot;&amp;gt;↓ added by Gas City, April 2026 · no longer about coding&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(50,472)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;490&quot; height=&quot;34&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;circle cx=&quot;22&quot; cy=&quot;17&quot; r=&quot;13&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot; fill=&quot;#fdfbf6&quot;&amp;gt;9&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;15&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot;&amp;gt;First deployed pack&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;29&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;a real business process runs itself&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(50,510)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;490&quot; height=&quot;34&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;circle cx=&quot;22&quot; cy=&quot;17&quot; r=&quot;13&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot; fill=&quot;#fdfbf6&quot;&amp;gt;10&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;15&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot;&amp;gt;Many deployed packs&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;29&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;you become the bottleneck&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(50,548)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;490&quot; height=&quot;34&quot; fill=&quot;#fdfbf6&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;circle cx=&quot;22&quot; cy=&quot;17&quot; r=&quot;13&quot; fill=&quot;#1c1a15&quot;/&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot; fill=&quot;#fdfbf6&quot;&amp;gt;11&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;15&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot;&amp;gt;Factory builder&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;48&quot; y=&quot;29&quot; font-size=&quot;11&quot; fill=&quot;#3a342a&quot;&amp;gt;a meta-orchestrator manages your packs&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(560,60)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;180&quot; height=&quot;164&quot; fill=&quot;#ecd0c2&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;text x=&quot;90&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#8a4a3a&quot; letter-spacing=&quot;0.08em&quot;&amp;gt;STAGES 1–4&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;90&quot; y=&quot;50&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;600&quot;&amp;gt;Don&apos;t even read&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;90&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;600&quot;&amp;gt;about Gas Town&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;90&quot; y=&quot;100&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#3a342a&quot;&amp;gt;framework solves problems&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;90&quot; y=&quot;114&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#3a342a&quot;&amp;gt;you haven&apos;t run into yet&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(560,246)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;180&quot; height=&quot;84&quot; fill=&quot;#f0d9b8&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;text x=&quot;90&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#8a6a3a&quot; letter-spacing=&quot;0.08em&quot;&amp;gt;STAGES 5–6&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;90&quot; y=&quot;48&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;600&quot;&amp;gt;Don&apos;t install yet&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;90&quot; y=&quot;68&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#3a342a&quot;&amp;gt;you&apos;ll wreck your repo&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(560,352)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;180&quot; height=&quot;234&quot; fill=&quot;#dde6d3&quot; stroke=&quot;#1c1a15&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;text x=&quot;90&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#5a6b48&quot; letter-spacing=&quot;0.08em&quot;&amp;gt;STAGES 7+&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;90&quot; y=&quot;50&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;600&quot;&amp;gt;Gas Town&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;90&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;600&quot;&amp;gt;becomes safe&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;14&quot; y=&quot;100&quot; font-size=&quot;10&quot; fill=&quot;#3a342a&quot;&amp;gt;You&apos;ve already felt the pain&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;14&quot; y=&quot;114&quot; font-size=&quot;10&quot; fill=&quot;#3a342a&quot;&amp;gt;Gas Town&apos;s design solves.&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;14&quot; y=&quot;128&quot; font-size=&quot;10&quot; fill=&quot;#3a342a&quot;&amp;gt;Each role earns its keep.&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;14&quot; y1=&quot;148&quot; x2=&quot;166&quot; y2=&quot;148&quot; stroke=&quot;#b8543e&quot; stroke-width=&quot;0.8&quot; stroke-dasharray=&quot;3 3&quot;/&amp;gt;
&amp;lt;text x=&quot;90&quot; y=&quot;170&quot; text-anchor=&quot;middle&quot; font-style=&quot;italic&quot; font-size=&quot;10&quot; fill=&quot;#b8543e&quot;&amp;gt;stages 9–11 are the&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;90&quot; y=&quot;184&quot; text-anchor=&quot;middle&quot; font-style=&quot;italic&quot; font-size=&quot;10&quot; fill=&quot;#b8543e&quot;&amp;gt;Gas City extension&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;90&quot; y=&quot;206&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot; fill=&quot;#3a342a&quot;&amp;gt;(no longer about code)&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g transform=&quot;translate(20,60)&quot;&amp;gt;
&amp;lt;line x1=&quot;0&quot; y1=&quot;0&quot; x2=&quot;0&quot; y2=&quot;520&quot; stroke=&quot;#8a8377&quot; stroke-width=&quot;0.8&quot;/&amp;gt;
&amp;lt;polygon points=&quot;-3,516 3,516 0,520&quot; fill=&quot;#8a8377&quot;/&amp;gt;
&amp;lt;text x=&quot;-12&quot; y=&quot;260&quot; text-anchor=&quot;middle&quot; font-size=&quot;9&quot; fill=&quot;#8a8377&quot; letter-spacing=&quot;0.12em&quot; transform=&quot;rotate(-90, -12, 260)&quot;&amp;gt;MORE AGENTS · MORE AUTONOMY&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 9.&amp;lt;/strong&amp;gt; Yegge&apos;s 11-stage ladder of AI-assisted coding adoption. Stages 1–8 existed at Gas Town&apos;s launch; stages 9–11 were added with Gas City in April 2026, when the ceiling moved from &quot;running your own orchestrator&quot; to &quot;running a fleet of deployable agent packs.&quot; The three safety zones are Yegge&apos;s, not mine: install Gas Town at Stage 7 or above, hold off at 5–6, and at 1–4 don&apos;t bother reading about it yet.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;Yegge&apos;s three cut points are blunt. &lt;strong&gt;Stage 7 and up:&lt;/strong&gt; install Gas Town. By the time you&apos;re hand-managing ten or more agents, you&apos;ve already lived inside the problems Gas Town&apos;s roles are designed for. The Refinery makes obvious sense. The Witness makes obvious sense. You&apos;ll know which knobs to leave alone. &lt;strong&gt;Stage 5–6:&lt;/strong&gt; don&apos;t install it. One agent, or a small handful, is fine; the orchestrator&apos;s overhead will eat you, and you&apos;ll fight machinery you don&apos;t yet need. He has &lt;a href=&quot;https://steve-yegge.medium.com/welcome-to-gas-town-4f25ee16dd04&quot;&gt;begged the community&lt;/a&gt;, repeatedly, not to do this. &lt;strong&gt;Stage 1–4:&lt;/strong&gt; don&apos;t even read about it. The framework is an answer to a question you haven&apos;t asked.&lt;/p&gt;
&lt;p&gt;The shape of the extension is its own story. Stages 9, 10, 11 are no longer about coding. They&apos;re the trajectory of someone who got coding-orchestration working and is now stamping out small autonomous packs for the rest of the business: image moderation, compliance review, contract triage. Stage 7 used to be the top of the ladder; the framework had a ceiling at &quot;run your own orchestrator.&quot; Gas City raised the ceiling, and what now sits above the old ceiling is no longer a developer-tools question. It&apos;s an operations question, and probably an org-design one.&lt;/p&gt;
&lt;p&gt;For a serious user, the most useful thing about the ladder is that it tells you not to skip rungs. The Stage 9 win comes from having a working Stage 7–8 system to put it on top of. If you&apos;re at Stage 6 today, the next thing to chase is not Gas City. It&apos;s making your work durable enough that any Stage 7–8 orchestrator (Gas Town or somebody else&apos;s) can run on it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Checkpoint: what you know now.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gas City is the SDK abstraction over Gas Town.&lt;/strong&gt; Roles, topology, and orchestration become declarative TOML packs. The same engine that runs a coding swarm can run image moderation, ops triage, anything you can describe as a graph of agents.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The strategic claim is SaaS replacement.&lt;/strong&gt; A non-technical Comms major has displaced a niche SaaS at her company with a pack. Tim Sehn rewrote 30,000 lines of Dolt&apos;s storage backend in C in a day, for $3,000 in API credits. The territory between those two points is the bet, not yet proven.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Where you are determines what you can install.&lt;/strong&gt; Yegge&apos;s 11-stage ladder is blunt: Stage 7+ for Gas Town, 5–6 for &quot;not yet,&quot; 1–4 for &quot;don&apos;t even read about it.&quot; The framework grew from 8 to 11 with Gas City because the top of the ladder is no longer about code.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Four ideas worth stealing&lt;/h2&gt;
&lt;h3&gt;Make the work durable, not the agent&lt;/h3&gt;
&lt;p&gt;This is the central inversion. In a system where any individual component (sessions, agents, even hosts) is unreliable, the way to get reliability is to push state into a substrate that doesn&apos;t share the unreliability. Gas Town&apos;s substrate is git. Yours might be a database, or a queue, or a state machine in S3. The principle is: agents should be stateless and replaceable, work state should be persistent and idempotent. If you&apos;re building anything multi-agent, the first design question is &quot;what does the work look like when no agent is currently holding it?&quot; If you can&apos;t answer cleanly, your system will lose work.&lt;/p&gt;
&lt;h3&gt;Acceptance criteria beat scripts when the worker is an LLM&lt;/h3&gt;
&lt;p&gt;NDI is the runtime version of this idea. If you write your workflows as imperative scripts that an LLM has to execute step-by-step, you&apos;ll spend forever debugging the cases where the LLM did something slightly different. If you write your workflows as a sequence of well-specified outcomes (&quot;this bead is done when X is true&quot;), the LLM has room to figure out how to satisfy the criteria, and you can verify completion mechanically. The MAKER paper&apos;s million-step result points at the same conclusion: long LLM workflows complete reliably when each step is atomic, locally checkable, and isolated from the others. Gas Town and MAKER are the production and academic versions of the same principle.&lt;/p&gt;
&lt;h3&gt;Reliability is a chain of watchers&lt;/h3&gt;
&lt;p&gt;Boot the Dog watches the Deacon. The Deacon watches the Witnesses. The Witnesses watch the Polecats. At every layer, the watcher is dumber and more reliable than the thing it&apos;s watching. This is how Gas Town keeps a swarm of LLMs aimed at a goal. The pattern is older than Gas Town (it&apos;s how kubelet watches pods, it&apos;s how supervisord watches processes) but it&apos;s particularly sharp here because the things being watched are smart, the watchers don&apos;t have to be, and the bottom of the chain is a deterministic three-minute heartbeat. If you&apos;re building anything where agents need to keep going without human oversight, the design question is not &quot;how do I make the agents more reliable?&quot; It&apos;s &quot;what&apos;s the simplest thing that can detect when an agent is stuck, and what does it do about it?&quot;&lt;/p&gt;
&lt;h3&gt;Topology should be declarative, not hardcoded&lt;/h3&gt;
&lt;p&gt;Gas Town v1 baked seven roles into the binary. Gas City pulled them out into TOML packs you can ship and version. The reason this matters: once you&apos;ve built one swarm that works, the second one shouldn&apos;t require rewriting the orchestrator. It should require writing a config. The crew that watches your image-moderation queue, the crew that runs your nightly compliance review, and the crew that triages customer tickets are the same engine with different topologies. If you&apos;re building anything multi-agent, the question after &quot;what does the work look like&quot; is &quot;what does the topology look like, and can someone who isn&apos;t an engineer change it?&quot; The orchestrators that win the next two years will be the ones where the answer is yes.&lt;/p&gt;
&lt;h2&gt;Epilogue&lt;/h2&gt;
&lt;p&gt;A year ago, the interesting engineering question in AI tooling was &quot;how do I make a single coding agent work well.&quot; The answer (RAG, tool use, larger contexts, better prompts) was technically deep and mostly commodity by the end of 2025.&lt;/p&gt;
&lt;p&gt;The interesting question this year is one layer up. &quot;How do I make a fleet of coding agents work well.&quot; The honest answer is that we don&apos;t fully know yet. Gas Town is one attempt, and it&apos;s a good one, but it&apos;s a 2026-shaped attempt. The model layer underneath it will keep moving. The agents will get more compliant about acting on their hooks without a fake keystroke. The session size will grow. The orchestrators will consolidate around two or three patterns and the rest will fade.&lt;/p&gt;
&lt;p&gt;What I think will not fade is the inversion at the heart of all of this. Work is persistent. Sessions are ephemeral. Agents are identities that connect work to sessions. Reliability comes from substrate, not from making the agents perfect. Acceptance criteria beat imperative scripts when the executor is a model. Watchers compose hierarchically. Topology is declarative. Whatever orchestrator we use a year from now, those facts will still be true.&lt;/p&gt;
&lt;p&gt;If you&apos;re at Stage 5 or 6 (one or a few coding agents, hand-managed) the lesson is not &quot;rush to install Gas Town.&quot; Yegge himself &lt;a href=&quot;https://steve-yegge.medium.com/welcome-to-gas-town-4f25ee16dd04&quot;&gt;begs you not to&lt;/a&gt;, repeatedly, on the grounds that you&apos;ll wreck your repo. The lesson is to start writing your work down. Get it out of your head and your chat history and into a thing the next session can read. Once your work is durable, you can swap in any orchestrator you want.&lt;/p&gt;
&lt;p&gt;We&apos;re going to start running this experiment ourselves at Boon, in a small way, this quarter. I&apos;ll write up what I learn.&lt;/p&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Welcome to Gas Town. Steve Yegge, January 2026 (&lt;a href=&quot;https://steve-yegge.medium.com/welcome-to-gas-town-4f25ee16dd04&quot;&gt;medium&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;The Future of Coding Agents. Steve Yegge, January 2026 (&lt;a href=&quot;https://steve-yegge.medium.com/the-future-of-coding-agents-e9451a84207c&quot;&gt;medium&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Welcome to the Wasteland. Steve Yegge, March 2026 (&lt;a href=&quot;https://steve-yegge.medium.com/welcome-to-the-wasteland-a-thousand-gas-towns-a5eb9bc8dc1f&quot;&gt;medium&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Gas Town: from Clown Show to v1.0. Steve Yegge, April 2026 (&lt;a href=&quot;https://steve-yegge.medium.com/gas-town-from-clown-show-to-v1-0-c239d9a407ec&quot;&gt;medium&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Welcome to Gas City. Steve Yegge, April 2026 (&lt;a href=&quot;https://steve-yegge.medium.com/welcome-to-gas-city-57f564bb3607&quot;&gt;medium&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;A Week In Gas Town. Tim Sehn (DoltHub), March 2026 (&lt;a href=&quot;https://www.dolthub.com/blog/2026-03-24-a-week-in-gas-town/&quot;&gt;dolthub blog&lt;/a&gt;). The Dolt-in-C case study cited above.&lt;/li&gt;
&lt;li&gt;Solving a Million-Step LLM Task with Zero Errors (MAKER). Meyerson et al., Cognizant + UT Austin, November 2025 (&lt;a href=&quot;https://arxiv.org/abs/2511.09030&quot;&gt;arXiv:2511.09030&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Gas Town source. &lt;a href=&quot;https://github.com/gastownhall/gastown&quot;&gt;github.com/gastownhall/gastown&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Gas City source. &lt;a href=&quot;https://github.com/gastownhall/gascity&quot;&gt;github.com/gastownhall/gascity&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Beads source. &lt;a href=&quot;https://github.com/gastownhall/beads&quot;&gt;github.com/gastownhall/beads&lt;/a&gt; (moved from &lt;code&gt;steveyegge/beads&lt;/code&gt; at v1.0).&lt;/li&gt;
&lt;li&gt;Dolt (git-versioned SQL database). &lt;a href=&quot;https://github.com/dolthub/dolt&quot;&gt;github.com/dolthub/dolt&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Bors-NG (the bisecting merge queue Gas Town&apos;s Refinery is modeled after). &lt;a href=&quot;https://github.com/bors-ng/bors-ng&quot;&gt;github.com/bors-ng/bors-ng&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>The Unified Multimodal Stack</title><link>https://www.augusteo.com/blog/omni-modal-stack/</link><guid isPermaLink="true">https://www.augusteo.com/blog/omni-modal-stack/</guid><description>How modern multimodal models are wired, and why moving from a vision backbone to a four-modality model takes more than plugging in more encoders.</description><pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import Figure from &quot;@components/figure/Figure.svelte&quot;;
import VideoTokenExplosion from &quot;@components/figures/omni-modal-stack/VideoTokenExplosion.svelte&quot;;
import EvsPatchPruning from &quot;@components/figures/omni-modal-stack/EvsPatchPruning.svelte&quot;;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;A sequel to &lt;a href=&quot;/blog/unified-vision-stack&quot;&gt;The Unified Vision Stack&lt;/a&gt;. Picks up where C-RADIOv4 ended and asks what it takes to teach one eye to listen, watch, and speak. Worked example throughout: NVIDIA&apos;s Nemotron 3 Nano Omni, released April 2026. About a 45-minute read.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;1. A single eye, three teachers&lt;/h3&gt;
&lt;p&gt;The previous post left us with C-RADIOv4. One vision backbone, three teachers, a single forward pass whose features could be read by three different downstream tasks at once.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 260&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; style=&quot;height: auto&quot;&amp;gt;
&amp;lt;g font-family=&quot;serif&quot; font-size=&quot;14&quot; font-weight=&quot;600&quot;&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;30&quot; width=&quot;110&quot; height=&quot;40&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;1.5&quot;/&amp;gt;&amp;lt;text x=&quot;85&quot; y=&quot;55&quot; text-anchor=&quot;middle&quot; fill=&quot;#2563EB&quot;&amp;gt;DINOv3&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;110&quot; width=&quot;110&quot; height=&quot;40&quot; fill=&quot;#D1FAE5&quot; stroke=&quot;#059669&quot; stroke-width=&quot;1.5&quot;/&amp;gt;&amp;lt;text x=&quot;85&quot; y=&quot;135&quot; text-anchor=&quot;middle&quot; fill=&quot;#059669&quot;&amp;gt;SigLIP2&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;190&quot; width=&quot;110&quot; height=&quot;40&quot; fill=&quot;#EDE0FC&quot; stroke=&quot;#9333EA&quot; stroke-width=&quot;1.5&quot;/&amp;gt;&amp;lt;text x=&quot;85&quot; y=&quot;215&quot; text-anchor=&quot;middle&quot; fill=&quot;#9333EA&quot;&amp;gt;SAM3&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;85&quot; y=&quot;18&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;TEACHERS&amp;lt;/text&amp;gt;
&amp;lt;g stroke=&quot;#8F8578&quot;&amp;gt;
&amp;lt;line x1=&quot;145&quot; y1=&quot;50&quot; x2=&quot;285&quot; y2=&quot;120&quot; marker-end=&quot;url(#a1)&quot;/&amp;gt;
&amp;lt;line x1=&quot;145&quot; y1=&quot;130&quot; x2=&quot;285&quot; y2=&quot;130&quot; marker-end=&quot;url(#a1)&quot;/&amp;gt;
&amp;lt;line x1=&quot;145&quot; y1=&quot;210&quot; x2=&quot;285&quot; y2=&quot;140&quot; marker-end=&quot;url(#a1)&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;defs&amp;gt;&amp;lt;marker id=&quot;a1&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;5&quot; markerHeight=&quot;5&quot; orient=&quot;auto&quot;&amp;gt;&amp;lt;path d=&quot;M0,0 L10,5 L0,10 z&quot; fill=&quot;#8F8578&quot;/&amp;gt;&amp;lt;/marker&amp;gt;&amp;lt;/defs&amp;gt;
&amp;lt;rect x=&quot;290&quot; y=&quot;100&quot; width=&quot;120&quot; height=&quot;60&quot; fill=&quot;#1A1A1A&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;350&quot; y=&quot;125&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;15&quot; font-weight=&quot;700&quot; fill=&quot;#F4EEE3&quot;&amp;gt;student&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;350&quot; y=&quot;145&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#B8651A&quot;&amp;gt;C-RADIOv4&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;412&quot; y1=&quot;130&quot; x2=&quot;432&quot; y2=&quot;130&quot; stroke=&quot;#8F8578&quot; marker-end=&quot;url(#a1)&quot;/&amp;gt;
&amp;lt;rect x=&quot;435&quot; y=&quot;115&quot; width=&quot;100&quot; height=&quot;30&quot; fill=&quot;none&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;485&quot; y=&quot;134&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#1A1A1A&quot;&amp;gt;unified feature&amp;lt;/text&amp;gt;
&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot;&amp;gt;
&amp;lt;line x1=&quot;540&quot; y1=&quot;125&quot; x2=&quot;565&quot; y2=&quot;55&quot; stroke=&quot;#8F8578&quot; marker-end=&quot;url(#a1)&quot;/&amp;gt;
&amp;lt;rect x=&quot;565&quot; y=&quot;40&quot; width=&quot;100&quot; height=&quot;30&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot;/&amp;gt;&amp;lt;text x=&quot;615&quot; y=&quot;59&quot; text-anchor=&quot;middle&quot; fill=&quot;#2563EB&quot;&amp;gt;DINOv3 head&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;540&quot; y1=&quot;130&quot; x2=&quot;565&quot; y2=&quot;130&quot; stroke=&quot;#8F8578&quot; marker-end=&quot;url(#a1)&quot;/&amp;gt;
&amp;lt;rect x=&quot;565&quot; y=&quot;115&quot; width=&quot;100&quot; height=&quot;30&quot; fill=&quot;#D1FAE5&quot; stroke=&quot;#059669&quot;/&amp;gt;&amp;lt;text x=&quot;615&quot; y=&quot;134&quot; text-anchor=&quot;middle&quot; fill=&quot;#059669&quot;&amp;gt;SigLIP head&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;540&quot; y1=&quot;135&quot; x2=&quot;565&quot; y2=&quot;205&quot; stroke=&quot;#8F8578&quot; marker-end=&quot;url(#a1)&quot;/&amp;gt;
&amp;lt;rect x=&quot;565&quot; y=&quot;190&quot; width=&quot;100&quot; height=&quot;30&quot; fill=&quot;#EDE0FC&quot; stroke=&quot;#9333EA&quot;/&amp;gt;&amp;lt;text x=&quot;615&quot; y=&quot;209&quot; text-anchor=&quot;middle&quot; fill=&quot;#9333EA&quot;&amp;gt;SAM3 head&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 1.&amp;lt;/strong&amp;gt; Where C-RADIOv4 came from. Three frozen teachers feed one student backbone, and three small heads on the output read off the kind of feature each downstream task wants. The student is what we&apos;re about to bolt the rest of an omni-modal stack onto.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/blog/unified-vision-stack&quot;&gt;The prequel post&lt;/a&gt; walks through C-RADIOv4 in detail: DINOv3 brought dense patch geometry, SigLIP2 brought text alignment, SAM3 brought instance masks, and C-RADIOv4 inherited the three through &lt;a href=&quot;https://arxiv.org/abs/2312.06709&quot;&gt;AM-RADIO-style&lt;/a&gt; agglomerative distillation. At inference you can attach a small head to the shared backbone and read off whichever family of feature a downstream task wants.&lt;/p&gt;
&lt;p&gt;For this post the relevant takeaway is narrower: the backbone is a strong &quot;vision-only&quot; model. Hand it an image and you can do dense prediction, retrieval, or segmentation through the appropriate head. Other vision tasks the family is reported to handle (depth, tracking, and beyond) are covered in the prequel; here we only need that the backbone can serve as a feature extractor for an LLM.&lt;/p&gt;
&lt;p&gt;But &quot;look at an image&quot; is not the job most modern systems are being built for. The thing people want is an agent: something that takes a photo, listens to a question, watches a video, and answers in language. The system has to perceive across modalities and then talk about what it perceived. C-RADIOv4 only handles half of that loop, and only for one of the modalities.&lt;/p&gt;
&lt;p&gt;What does the rest take?&lt;/p&gt;
&lt;p&gt;There&apos;s a clue in C-RADIOv4 itself. One of its teachers was SigLIP2, so the SigLIP-aligned head on the backbone is trained to put image features into a space shared with text embeddings. SigLIP itself trains image and caption towers to score together (more on the loss in §3); inheriting that head means the C-RADIOv4 backbone can produce vectors that are aligned with caption-space text in the &lt;a href=&quot;https://arxiv.org/abs/2303.15343&quot;&gt;SigLIP&lt;/a&gt; sense. We already have, through that head, an image encoder that &quot;speaks text&quot; in that specific sense.&lt;/p&gt;
&lt;p&gt;So why does Nemotron 3 Nano Omni, the open omni model NVIDIA shipped in April 2026, still bolt a separate text tokenizer onto the front of a 30-billion-parameter autoregressive language model and feed C-RADIOv4-H&apos;s features into it as if they were just another input? If the SigLIP head was meant to be the text bridge, what does the LLM do that the head can&apos;t?&lt;/p&gt;
&lt;p&gt;The short answer is that SigLIP-style alignment scores; it doesn&apos;t generate. Once that&apos;s clear, the rest of the architecture falls into place. The post unfolds three architectural moves: a text-centric autoregressive decoder at the center, modality-specific encoders for everything that isn&apos;t text, and a stack of token-budget mechanisms to keep inference cost from collapsing under its own weight.&lt;/p&gt;
&lt;h2&gt;Act 1: the puzzle&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Why a vision backbone that already aligns to text still cannot answer in language.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;2. The SigLIP puzzle&lt;/h3&gt;
&lt;p&gt;The SigLIP head sitting on top of C-RADIOv4 produces caption-aligned features. An image goes in, a vector comes out, and that vector lives in the same embedding space SigLIP&apos;s text tower lives in. So you can take any sentence, embed it through the text tower, and ask: how close is its embedding to the image&apos;s? You get a similarity score. Higher score, the caption fits the image better. Lower score, it doesn&apos;t.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 310&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; style=&quot;height: auto&quot;&amp;gt;
&amp;lt;defs&amp;gt;&amp;lt;marker id=&quot;a2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;5&quot; markerHeight=&quot;5&quot; orient=&quot;auto&quot;&amp;gt;&amp;lt;path d=&quot;M0,0 L10,5 L0,10 z&quot; fill=&quot;#6B6258&quot;/&amp;gt;&amp;lt;/marker&amp;gt;&amp;lt;/defs&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;170&quot; y=&quot;20&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;CONTRASTIVE (SIGLIP / CLIP)&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;30&quot; y=&quot;40&quot; width=&quot;80&quot; height=&quot;80&quot; fill=&quot;#EDE5D4&quot; stroke=&quot;#8F8578&quot;/&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;40&quot; width=&quot;80&quot; height=&quot;32&quot; fill=&quot;#93C5FD&quot; opacity=&quot;.7&quot;/&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;72&quot; width=&quot;80&quot; height=&quot;22&quot; fill=&quot;#D1AE7A&quot; opacity=&quot;.8&quot;/&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;94&quot; width=&quot;80&quot; height=&quot;26&quot; fill=&quot;#059669&quot; opacity=&quot;.6&quot;/&amp;gt;
&amp;lt;circle cx=&quot;90&quot; cy=&quot;55&quot; r=&quot;8&quot; fill=&quot;#FEF3C7&quot;/&amp;gt;
&amp;lt;text x=&quot;70&quot; y=&quot;135&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;image&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;30&quot; y=&quot;170&quot; width=&quot;180&quot; height=&quot;28&quot; fill=&quot;#F9F5EB&quot; stroke=&quot;#C9BEAA&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;120&quot; y=&quot;188&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; fill=&quot;#1A1A1A&quot;&amp;gt;&quot;a dog on a beach&quot;&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;120&quot; y=&quot;213&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;caption&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;115&quot; y1=&quot;80&quot; x2=&quot;155&quot; y2=&quot;80&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a2)&quot;/&amp;gt;
&amp;lt;line x1=&quot;215&quot; y1=&quot;184&quot; x2=&quot;245&quot; y2=&quot;184&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a2)&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;160&quot; y=&quot;68&quot; width=&quot;100&quot; height=&quot;22&quot; fill=&quot;none&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;210&quot; y=&quot;83&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#2563EB&quot;&amp;gt;[0.7, -0.1, ...]&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;248&quot; y=&quot;172&quot; width=&quot;100&quot; height=&quot;22&quot; fill=&quot;none&quot; stroke=&quot;#059669&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;298&quot; y=&quot;187&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#059669&quot;&amp;gt;[0.6, 0.0, ...]&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;circle cx=&quot;295&quot; cy=&quot;105&quot; r=&quot;22&quot; fill=&quot;#1A1A1A&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;295&quot; y=&quot;113&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;18&quot; fill=&quot;#F4EEE3&quot;&amp;gt;·&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;265&quot; y1=&quot;83&quot; x2=&quot;278&quot; y2=&quot;98&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a2)&quot;/&amp;gt;
&amp;lt;line x1=&quot;295&quot; y1=&quot;172&quot; x2=&quot;295&quot; y2=&quot;130&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a2)&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;378&quot; y=&quot;110&quot; text-anchor=&quot;end&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;13&quot; fill=&quot;#1A1A1A&quot;&amp;gt;= 0.81&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;295&quot; y=&quot;244&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;one number per pair&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;295&quot; y=&quot;261&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;&quot;does this caption match&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;295&quot; y=&quot;275&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;this image?&quot;&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;382&quot; y1=&quot;40&quot; x2=&quot;382&quot; y2=&quot;285&quot; stroke=&quot;#C9BEAA&quot; stroke-dasharray=&quot;3,3&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;525&quot; y=&quot;20&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;AUTOREGRESSIVE (LLM)&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot;&amp;gt;
&amp;lt;rect x=&quot;385&quot; y=&quot;50&quot; width=&quot;36&quot; height=&quot;22&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot;/&amp;gt;&amp;lt;text x=&quot;403&quot; y=&quot;65&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;the&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;423&quot; y=&quot;50&quot; width=&quot;38&quot; height=&quot;22&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot;/&amp;gt;&amp;lt;text x=&quot;442&quot; y=&quot;65&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;dog&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;463&quot; y=&quot;50&quot; width=&quot;28&quot; height=&quot;22&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot;/&amp;gt;&amp;lt;text x=&quot;477&quot; y=&quot;65&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;is&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;493&quot; y=&quot;50&quot; width=&quot;32&quot; height=&quot;22&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot;/&amp;gt;&amp;lt;text x=&quot;509&quot; y=&quot;65&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;on&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;527&quot; y=&quot;50&quot; width=&quot;36&quot; height=&quot;22&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot;/&amp;gt;&amp;lt;text x=&quot;545&quot; y=&quot;65&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;the&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;565&quot; y=&quot;50&quot; width=&quot;34&quot; height=&quot;22&quot; fill=&quot;none&quot; stroke=&quot;#B91C1C&quot; stroke-dasharray=&quot;3,2&quot;/&amp;gt;&amp;lt;text x=&quot;582&quot; y=&quot;65&quot; text-anchor=&quot;middle&quot; fill=&quot;#B91C1C&quot;&amp;gt;?&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;492&quot; y=&quot;91&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;prefix&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;405&quot; y=&quot;115&quot; width=&quot;180&quot; height=&quot;40&quot; fill=&quot;#1A1A1A&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;495&quot; y=&quot;140&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#F4EEE3&quot;&amp;gt;LLM&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;492&quot; y1=&quot;78&quot; x2=&quot;492&quot; y2=&quot;113&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a2)&quot;/&amp;gt;
&amp;lt;line x1=&quot;492&quot; y1=&quot;156&quot; x2=&quot;492&quot; y2=&quot;178&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a2)&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;492&quot; y=&quot;194&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;P(NEXT TOKEN)&amp;lt;/text&amp;gt;
&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot;&amp;gt;
&amp;lt;rect x=&quot;395&quot; y=&quot;200&quot; width=&quot;14&quot; height=&quot;44&quot; fill=&quot;#2563EB&quot;/&amp;gt;&amp;lt;text x=&quot;402&quot; y=&quot;252&quot; text-anchor=&quot;start&quot; fill=&quot;#1A1A1A&quot; transform=&quot;rotate(35 402 252)&quot;&amp;gt;beach&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;412&quot; y=&quot;218&quot; width=&quot;14&quot; height=&quot;26&quot; fill=&quot;#2563EB&quot; opacity=&quot;.7&quot;/&amp;gt;&amp;lt;text x=&quot;419&quot; y=&quot;252&quot; text-anchor=&quot;start&quot; fill=&quot;#1A1A1A&quot; transform=&quot;rotate(35 419 252)&quot;&amp;gt;grass&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;429&quot; y=&quot;223&quot; width=&quot;14&quot; height=&quot;21&quot; fill=&quot;#2563EB&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;text x=&quot;436&quot; y=&quot;252&quot; text-anchor=&quot;start&quot; fill=&quot;#1A1A1A&quot; transform=&quot;rotate(35 436 252)&quot;&amp;gt;floor&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;446&quot; y=&quot;229&quot; width=&quot;14&quot; height=&quot;15&quot; fill=&quot;#2563EB&quot; opacity=&quot;.4&quot;/&amp;gt;&amp;lt;text x=&quot;453&quot; y=&quot;252&quot; text-anchor=&quot;start&quot; fill=&quot;#1A1A1A&quot; transform=&quot;rotate(35 453 252)&quot;&amp;gt;car&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;463&quot; y=&quot;232&quot; width=&quot;14&quot; height=&quot;12&quot; fill=&quot;#2563EB&quot; opacity=&quot;.3&quot;/&amp;gt;&amp;lt;text x=&quot;470&quot; y=&quot;252&quot; text-anchor=&quot;start&quot; fill=&quot;#1A1A1A&quot; transform=&quot;rotate(35 470 252)&quot;&amp;gt;deck&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;480&quot; y=&quot;236&quot; width=&quot;14&quot; height=&quot;8&quot; fill=&quot;#2563EB&quot; opacity=&quot;.22&quot;/&amp;gt;&amp;lt;text x=&quot;487&quot; y=&quot;252&quot; text-anchor=&quot;start&quot; fill=&quot;#1A1A1A&quot; transform=&quot;rotate(35 487 252)&quot;&amp;gt;sand&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;497&quot; y=&quot;238&quot; width=&quot;14&quot; height=&quot;6&quot; fill=&quot;#2563EB&quot; opacity=&quot;.18&quot;/&amp;gt;&amp;lt;text x=&quot;504&quot; y=&quot;252&quot; text-anchor=&quot;start&quot; fill=&quot;#1A1A1A&quot; transform=&quot;rotate(35 504 252)&quot;&amp;gt;moon&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;514&quot; y=&quot;240&quot; width=&quot;14&quot; height=&quot;4&quot; fill=&quot;#2563EB&quot; opacity=&quot;.15&quot;/&amp;gt;&amp;lt;text x=&quot;521&quot; y=&quot;252&quot; text-anchor=&quot;start&quot; fill=&quot;#1A1A1A&quot; transform=&quot;rotate(35 521 252)&quot;&amp;gt;...&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;615&quot; y=&quot;222&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;over&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;615&quot; y=&quot;234&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;~100k&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;615&quot; y=&quot;246&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;tokens&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;405&quot; y=&quot;285&quot; width=&quot;60&quot; height=&quot;22&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;435&quot; y=&quot;300&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#2563EB&quot;&amp;gt;beach&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;510&quot; y=&quot;300&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;sample, append, repeat&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 2.&amp;lt;/strong&amp;gt; Two different operations on the same example. Left: SigLIP-style scoring takes an image and a caption, projects each into a shared space, and outputs a single number. Right: an LLM takes a partial sentence and outputs a probability distribution over what the next token should be, then samples one and appends it. Scaling SigLIP doesn&apos;t get you the right side. Scaling an LLM doesn&apos;t, by itself, get you the left side.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;What you can&apos;t do with that score is ask for a description of the image. The SigLIP text tower never learned to &lt;em&gt;produce&lt;/em&gt; sentences. It only learned to &lt;em&gt;score&lt;/em&gt; them.&lt;/p&gt;
&lt;p&gt;This is easy to lose track of, because SigLIP and CLIP are routinely described as &quot;vision-language models&quot; in a way that suggests they understand language in some general sense. They don&apos;t, in the sense the rest of the post is going to need. Here&apos;s what they actually do, taken straight from the &lt;a href=&quot;https://arxiv.org/abs/2103.00020&quot;&gt;CLIP paper&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;The text sequence is bracketed with [SOS] and [EOS] tokens and the activations of the highest layer of the transformer at the [EOS] token are treated as the feature representation of the text which is layer normalized and then linearly projected into the multi-modal embedding space.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The text tower runs through the sentence once, takes one vector from one position in the final layer, and stops. Whatever happened in the per-token states gets thrown away. The output is a single embedding that says &quot;this sentence as a whole means X&quot; in some compressed form, and that is the entire output. The training loss never asked the model to expose a per-token next-token distribution, so it never learned to produce one.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://arxiv.org/abs/2303.15343&quot;&gt;SigLIP&lt;/a&gt; and &lt;a href=&quot;https://arxiv.org/abs/2502.14786&quot;&gt;SigLIP 2&lt;/a&gt; keep the same shape, just with different losses (sigmoid binary classification per pair instead of softmax over the batch). SigLIP 2 even attaches a &lt;em&gt;decoder&lt;/em&gt; during training to push the encoder representations harder, but the decoder gets thrown away before release:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Finally, we note that the decoder only serves for representation learning here and is not part of the model release.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So the artifact you actually get is still a contrastive embedder. It scores. If you want to generate, you need a different organ.&lt;/p&gt;
&lt;p&gt;The architectural consequence is the central design choice of every modern omni-modal stack. The text decoder sits in the middle. Vision goes in as features. Audio goes in as features. Video goes in as features. The decoder is the only thing in the system that can run a softmax over the next word, and everything else feeds it.&lt;/p&gt;
&lt;p&gt;C-RADIOv4 is one of the best things you can put in front of a decoder. It is not, by itself, a decoder.&lt;/p&gt;
&lt;h3&gt;3. Contrastive vs autoregressive&lt;/h3&gt;
&lt;p&gt;The split between scoring and generating is structural, not a difference of degree. To see why, look at what each model is being trained to optimize.&lt;/p&gt;
&lt;p&gt;CLIP&apos;s loss is the cleanest version. Take a batch of N image-caption pairs. Run each image through the image tower, each caption through the text tower. You now have N image vectors and N text vectors. Compute every pairwise dot product: that gives you an N×N matrix of similarities. The training loss says, for each row of this matrix, the diagonal entry should be the largest (because the image at row &lt;em&gt;i&lt;/em&gt; pairs with the caption at column &lt;em&gt;i&lt;/em&gt;, the matched pair). For each column, same thing. Apply softmax cross-entropy over the rows and over the columns, average them. That&apos;s the whole loss.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Given a batch of N (image, text) pairs, CLIP is trained to predict which of the N × N possible (image, text) pairings across a batch actually occurred. ... We optimize a symmetric cross entropy loss over these similarity scores.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;SigLIP swaps the softmax for a sigmoid binary classification on each cell of the matrix: each pair is either matched (label +1) or unmatched (label −1), and every cell contributes independently. Computationally cheaper to scale. Same architectural shape: dual towers, dot product, scalar score per pair.&lt;/p&gt;
&lt;p&gt;What this loss does &lt;em&gt;not&lt;/em&gt; teach the model is anything about &lt;em&gt;next words&lt;/em&gt;. The matched-pair signal at any cell tells the towers &quot;make these two embeddings closer.&quot; It says nothing about what should come after a given word in a sentence. Both towers, image and text, are trained to produce one embedding each, then compared. The text tower never has to predict the next token. There is no decoder loss. There is no autoregressive supervision anywhere in the training procedure.&lt;/p&gt;
&lt;p&gt;A language model is doing the opposite thing. Take a sentence. For each prefix of the sentence (the first 1 word, the first 2 words, and so on), compute a probability distribution over what the next word should be. The loss is the negative log-likelihood of the actual next word, summed over all positions. This &lt;em&gt;does&lt;/em&gt; teach the model about next words. It teaches almost only about next words. After enough data, you have a function that takes a partial sentence and returns a probability distribution over every possible continuation, which you can sample from to make new text.&lt;/p&gt;
&lt;p&gt;These are two different functions, and the difference doesn&apos;t shrink with scale. The contrastive function takes (image, sentence) and returns a number. The autoregressive function takes (partial sentence) and returns a probability distribution over the next token. You can&apos;t use one to do the other&apos;s job, no matter how large you make it. Scaling SigLIP doesn&apos;t get you generation. Scaling an LLM doesn&apos;t, by itself, get you a calibrated image-caption similarity score; you have to fit a head on top to extract one.&lt;/p&gt;
&lt;p&gt;This is why most production multimodal stacks ship both. SigLIP-class encoders for &lt;em&gt;understanding&lt;/em&gt; what&apos;s in an image. An autoregressive LLM in the middle for &lt;em&gt;producing&lt;/em&gt; language. (Some research designs go the other way, with an early-fusion model that tokenizes images straight into the LLM&apos;s vocabulary; &lt;a href=&quot;https://arxiv.org/abs/2405.09818&quot;&gt;Chameleon&lt;/a&gt; is the cleanest example of that branch. The pretraining cost story for that approach is much larger, and it isn&apos;t the path Nemotron 3 Nano Omni took.)&lt;/p&gt;
&lt;h3&gt;4. The text decoder is a different organ&lt;/h3&gt;
&lt;p&gt;The five things an autoregressive LLM contains, in the order an input flows through them: a tokenizer that splits text into sub-word pieces from a fixed vocabulary; a learned embedding table that maps token IDs to continuous vectors; a stack of attention (and sometimes state-space) blocks that produce contextualized hidden states; a learned LM head that projects each hidden state back to the vocabulary as logits; and an autoregressive sampling loop that picks one token from the final logits, appends it to the input, and runs the whole thing again.&lt;/p&gt;
&lt;p&gt;Pieces 4 and 5 do not exist in SigLIP&apos;s text tower. SigLIP has the first two (tokenizer, embedding table) and &lt;em&gt;something like&lt;/em&gt; the third (a small transformer over the tokens), but the output gets projected once into the shared image-text space and then the model stops. There is no head that produces a distribution over next tokens, and no sampling loop to consume one. The contrastive training objective specifically did not select for them.&lt;/p&gt;
&lt;p&gt;So we need an LLM in the middle. The question becomes how to get the vision encoder&apos;s output into the LLM&apos;s input, and the parallel question for audio and video.&lt;/p&gt;
&lt;h2&gt;Act 2: three ways to wire it up&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Once you know you need a generative LLM, the question is how you feed it.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;5. The adapter zoo: Flamingo, BLIP-2, LLaVA&lt;/h3&gt;
&lt;p&gt;Suppose you have a strong text-only LLM. Pretraining cost millions of dollars and you don&apos;t want to redo it. You also have a strong vision encoder, frozen, that produces dense feature maps. The question is: how do you bolt them together so the LLM can answer questions about images without losing what it already knows about language?&lt;/p&gt;
&lt;p&gt;Three approaches dominated the 2022-2024 era, and the differences between them turn out to be load-bearing for what came after.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 330&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; style=&quot;height: auto&quot;&amp;gt;
&amp;lt;defs&amp;gt;&amp;lt;marker id=&quot;a3&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;5&quot; markerHeight=&quot;5&quot; orient=&quot;auto&quot;&amp;gt;&amp;lt;path d=&quot;M0,0 L10,5 L0,10 z&quot; fill=&quot;#6B6258&quot;/&amp;gt;&amp;lt;/marker&amp;gt;&amp;lt;/defs&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;113&quot; y=&quot;20&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;14&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;Flamingo&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;20&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;14&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;BLIP-2&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;567&quot; y=&quot;20&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;14&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;LLaVA&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;40&quot; y=&quot;35&quot; width=&quot;146&quot; height=&quot;34&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;113&quot; y=&quot;51&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; fill=&quot;#2563EB&quot;&amp;gt;Vision encoder&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;113&quot; y=&quot;64&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;(frozen)&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;113&quot; y1=&quot;72&quot; x2=&quot;113&quot; y2=&quot;88&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a3)&quot;/&amp;gt;
&amp;lt;rect x=&quot;50&quot; y=&quot;88&quot; width=&quot;126&quot; height=&quot;30&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;text x=&quot;113&quot; y=&quot;103&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; fill=&quot;#B8651A&quot;&amp;gt;Perceiver Resampler&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;113&quot; y=&quot;115&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;→ 64 tokens&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;113&quot; y1=&quot;120&quot; x2=&quot;113&quot; y2=&quot;138&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a3)&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;138&quot; width=&quot;146&quot; height=&quot;148&quot; fill=&quot;none&quot; stroke=&quot;#1A1A1A&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;113&quot; y=&quot;155&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#1A1A1A&quot;&amp;gt;LLM (frozen)&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;50&quot; y=&quot;162&quot; width=&quot;126&quot; height=&quot;13&quot; fill=&quot;#F4EEE3&quot;/&amp;gt;
&amp;lt;rect x=&quot;50&quot; y=&quot;178&quot; width=&quot;126&quot; height=&quot;13&quot; fill=&quot;#FEE7D5&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot; stroke-dasharray=&quot;2,2&quot;/&amp;gt;
&amp;lt;rect x=&quot;50&quot; y=&quot;194&quot; width=&quot;126&quot; height=&quot;13&quot; fill=&quot;#F4EEE3&quot;/&amp;gt;
&amp;lt;rect x=&quot;50&quot; y=&quot;210&quot; width=&quot;126&quot; height=&quot;13&quot; fill=&quot;#FEE7D5&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot; stroke-dasharray=&quot;2,2&quot;/&amp;gt;
&amp;lt;rect x=&quot;50&quot; y=&quot;226&quot; width=&quot;126&quot; height=&quot;13&quot; fill=&quot;#F4EEE3&quot;/&amp;gt;
&amp;lt;rect x=&quot;50&quot; y=&quot;242&quot; width=&quot;126&quot; height=&quot;13&quot; fill=&quot;#FEE7D5&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot; stroke-dasharray=&quot;2,2&quot;/&amp;gt;
&amp;lt;rect x=&quot;50&quot; y=&quot;258&quot; width=&quot;126&quot; height=&quot;13&quot; fill=&quot;#F4EEE3&quot;/&amp;gt;
&amp;lt;text x=&quot;113&quot; y=&quot;304&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#B8651A&quot;&amp;gt;tanh(α=0) at init&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;113&quot; y=&quot;318&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;cross-attn inserted&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;267&quot; y=&quot;35&quot; width=&quot;146&quot; height=&quot;34&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;51&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; fill=&quot;#2563EB&quot;&amp;gt;Vision encoder&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;64&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;(frozen)&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;340&quot; y1=&quot;72&quot; x2=&quot;340&quot; y2=&quot;88&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a3)&quot;/&amp;gt;
&amp;lt;rect x=&quot;267&quot; y=&quot;88&quot; width=&quot;146&quot; height=&quot;62&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;104&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; fill=&quot;#B8651A&quot;&amp;gt;Q-Former (188M)&amp;lt;/text&amp;gt;
&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot;&amp;gt;
&amp;lt;rect x=&quot;277&quot; y=&quot;113&quot; width=&quot;18&quot; height=&quot;20&quot; fill=&quot;#FEF3C7&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;297&quot; y=&quot;113&quot; width=&quot;18&quot; height=&quot;20&quot; fill=&quot;#FEF3C7&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;317&quot; y=&quot;113&quot; width=&quot;18&quot; height=&quot;20&quot; fill=&quot;#FEF3C7&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;337&quot; y=&quot;113&quot; width=&quot;18&quot; height=&quot;20&quot; fill=&quot;#FEF3C7&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;357&quot; y=&quot;113&quot; width=&quot;18&quot; height=&quot;20&quot; fill=&quot;#FEF3C7&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;text x=&quot;390&quot; y=&quot;127&quot; fill=&quot;#6B6258&quot;&amp;gt;...&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;146&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;32 learned queries&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;340&quot; y1=&quot;152&quot; x2=&quot;340&quot; y2=&quot;170&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a3)&quot;/&amp;gt;
&amp;lt;rect x=&quot;267&quot; y=&quot;170&quot; width=&quot;146&quot; height=&quot;116&quot; fill=&quot;none&quot; stroke=&quot;#1A1A1A&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;187&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#1A1A1A&quot;&amp;gt;LLM (frozen)&amp;lt;/text&amp;gt;
&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot;&amp;gt;
&amp;lt;rect x=&quot;277&quot; y=&quot;195&quot; width=&quot;18&quot; height=&quot;14&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;296&quot; y=&quot;195&quot; width=&quot;18&quot; height=&quot;14&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;315&quot; y=&quot;195&quot; width=&quot;18&quot; height=&quot;14&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;334&quot; y=&quot;195&quot; width=&quot;18&quot; height=&quot;14&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;353&quot; y=&quot;195&quot; width=&quot;18&quot; height=&quot;14&quot; fill=&quot;#F4EEE3&quot; stroke=&quot;#1A1A1A&quot; stroke-width=&quot;0.4&quot;/&amp;gt;
&amp;lt;rect x=&quot;372&quot; y=&quot;195&quot; width=&quot;18&quot; height=&quot;14&quot; fill=&quot;#F4EEE3&quot; stroke=&quot;#1A1A1A&quot; stroke-width=&quot;0.4&quot;/&amp;gt;
&amp;lt;rect x=&quot;391&quot; y=&quot;195&quot; width=&quot;18&quot; height=&quot;14&quot; fill=&quot;#F4EEE3&quot; stroke=&quot;#1A1A1A&quot; stroke-width=&quot;0.4&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;227&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;queries → soft tokens&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;242&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;prepended to prompt&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;304&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#B8651A&quot;&amp;gt;2-stage training&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;318&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;connector module&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;494&quot; y=&quot;35&quot; width=&quot;146&quot; height=&quot;34&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;567&quot; y=&quot;51&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; fill=&quot;#2563EB&quot;&amp;gt;CLIP encoder&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;567&quot; y=&quot;64&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;(frozen)&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;567&quot; y1=&quot;72&quot; x2=&quot;567&quot; y2=&quot;88&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a3)&quot;/&amp;gt;
&amp;lt;rect x=&quot;494&quot; y=&quot;88&quot; width=&quot;146&quot; height=&quot;30&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;text x=&quot;567&quot; y=&quot;103&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; fill=&quot;#B8651A&quot;&amp;gt;Linear / 2-layer MLP&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;567&quot; y=&quot;115&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot;&amp;gt;W · Z_v&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;567&quot; y1=&quot;120&quot; x2=&quot;567&quot; y2=&quot;138&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a3)&quot;/&amp;gt;
&amp;lt;rect x=&quot;494&quot; y=&quot;138&quot; width=&quot;146&quot; height=&quot;148&quot; fill=&quot;none&quot; stroke=&quot;#1A1A1A&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;567&quot; y=&quot;156&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#1A1A1A&quot;&amp;gt;LLM (frozen, ph 1)&amp;lt;/text&amp;gt;
&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot;&amp;gt;
&amp;lt;rect x=&quot;504&quot; y=&quot;166&quot; width=&quot;18&quot; height=&quot;14&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;523&quot; y=&quot;166&quot; width=&quot;18&quot; height=&quot;14&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;542&quot; y=&quot;166&quot; width=&quot;18&quot; height=&quot;14&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;561&quot; y=&quot;166&quot; width=&quot;18&quot; height=&quot;14&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;580&quot; y=&quot;166&quot; width=&quot;18&quot; height=&quot;14&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;599&quot; y=&quot;166&quot; width=&quot;18&quot; height=&quot;14&quot; fill=&quot;#F4EEE3&quot; stroke=&quot;#1A1A1A&quot; stroke-width=&quot;0.4&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;567&quot; y=&quot;206&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;vision tokens prepended&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;567&quot; y=&quot;220&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;to text prompt&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;567&quot; y=&quot;304&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#B8651A&quot;&amp;gt;single matrix W&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;567&quot; y=&quot;318&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;simplest connector&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 3.&amp;lt;/strong&amp;gt; The same problem solved three ways. Flamingo inserts new gated cross-attention layers (rust, dashed) into a frozen LLM and starts them at zero with &lt;code&gt;tanh(α=0)&lt;/code&gt;. BLIP-2 hangs a 188M-parameter Q-Former between two frozen models, with 32 learned query embeddings. LLaVA throws away the connector machinery and just multiplies CLIP&apos;s output through an MLP into the LLM&apos;s token space. All three keep the LLM frozen during pretraining; the differences are about how vision gets into the prompt.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://arxiv.org/abs/2204.14198&quot;&gt;Flamingo&lt;/a&gt; (DeepMind, 2022) inserts new layers into the frozen LLM. Specifically, between every pretrained block, Flamingo adds a &lt;code&gt;GATED XATTN-DENSE&lt;/code&gt; block: a cross-attention layer where the query is the LLM&apos;s current hidden state and the key/value are visual features (after passing through a Perceiver Resampler that compresses the variable-size feature grid into 64 fixed slots). The cross-attention output is gated by a learned scalar α, multiplied through &lt;code&gt;tanh(α)&lt;/code&gt; where α is initialized to zero. At step 0, every new block is a no-op:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;We freeze the pretrained LM blocks, and insert gated cross-attention dense blocks between the original layers, trained from scratch. To ensure that at initialization, the conditioned model yields the same results as the original language model, we use a tanh-gating mechanism.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The training process pushes α off zero only as the cross-attention starts contributing useful signal. The LLM weights themselves are never updated. Catastrophic forgetting is averted by construction.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://arxiv.org/abs/2301.12597&quot;&gt;BLIP-2&lt;/a&gt; (Salesforce, 2023) takes a different shape. Instead of adding cross-attention into the LLM, it adds a separate small module (a 188-million-parameter Querying Transformer, or Q-Former) between the frozen vision encoder and the frozen LLM. Q-Former has a fixed set of learned &quot;query&quot; embeddings that attend to the vision encoder&apos;s outputs through cross-attention. The output is a short fixed-length sequence (32 vectors in the released checkpoints) that gets linearly projected and prepended to the LLM&apos;s prompt as if it were ordinary text tokens. Q-Former is trained in two stages: first in isolation against an image-text alignment objective, then together with the frozen LLM against a generation loss.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;BLIP-2 bridges the modality gap with a lightweight Querying Transformer, which is pre-trained in two stages. The first stage bootstraps vision-language representation learning from a frozen image encoder. The second stage bootstraps vision-to-language generative learning from a frozen language model.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://arxiv.org/abs/2304.08485&quot;&gt;LLaVA&lt;/a&gt; (Microsoft and Wisconsin, 2023) is the simplest possible version of the same idea. Skip the cross-attention. Skip the Q-Former. Take the dense feature map from a frozen CLIP encoder, multiply it by a single trainable matrix &lt;code&gt;W&lt;/code&gt; into the LLM&apos;s word-embedding dimension, and prepend the resulting vectors to the prompt. That&apos;s it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;We consider a simple linear layer to connect image features into the word embedding space. Specifically, we apply a trainable projection matrix W to convert Z_v into language embedding tokens H_v, which have the same dimensionality as the word embedding space in the language model.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://arxiv.org/abs/2310.03744&quot;&gt;LLaVA-1.5&lt;/a&gt; upgraded the projector from a single linear layer to a two-layer MLP and reported gains, but the architectural shape stayed the same. One small module. No new layers in the LLM. The LLM weights are still frozen during stage-1 alignment training; they do get fine-tuned during stage-2 instruction tuning, but they never see the gradient of a &lt;em&gt;pretraining&lt;/em&gt; loss applied to a multimodal sequence, only an instruction-tuning loss applied at the end.&lt;/p&gt;
&lt;p&gt;The pattern across all three: the LLM was trained on text alone. Vision is a side-loaded computation, translated into something that looks like text tokens (or, in Flamingo&apos;s case, attended to as cross-attention values), and the LLM consumes that translation. The LLM never learns the joint distribution over multimodal sequences during its own pretraining. Whatever multimodal understanding shows up has to come through the projector and the late-stage fine-tune.&lt;/p&gt;
&lt;p&gt;A note on Qwen2.5-VL, which sometimes gets lumped in with this group. The Qwen team uses the LLaVA-style MLP projector pattern, but they train the LLM jointly with a from-scratch dynamic-resolution ViT. So Qwen2.5-VL inherits the &lt;em&gt;connector design&lt;/em&gt; from LLaVA but the &lt;em&gt;training pattern&lt;/em&gt; from the next section&apos;s native-multimodal approach.&lt;/p&gt;
&lt;h3&gt;6. Native multimodal: one decoder, one sequence&lt;/h3&gt;
&lt;p&gt;The structural alternative is to drop the &quot;frozen LLM&quot; constraint. Instead of bolting a vision adapter onto a finished language model, you train one transformer decoder on a single token stream that contains both modalities from the start. The LLM doesn&apos;t have to be told &quot;now you&apos;re seeing an image&quot; through a side-channel; the multimodal sequences are part of its pretraining distribution.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 320&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; style=&quot;height: auto&quot;&amp;gt;
&amp;lt;defs&amp;gt;&amp;lt;marker id=&quot;a4&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;5&quot; markerHeight=&quot;5&quot; orient=&quot;auto&quot;&amp;gt;&amp;lt;path d=&quot;M0,0 L10,5 L0,10 z&quot; fill=&quot;#6B6258&quot;/&amp;gt;&amp;lt;/marker&amp;gt;&amp;lt;/defs&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;170&quot; y=&quot;20&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;13&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;Adapter (LLaVA, Flamingo)&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;35&quot; y=&quot;44&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;PRETRAINING SEQUENCE&amp;lt;/text&amp;gt;
&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot;&amp;gt;
&amp;lt;rect x=&quot;35&quot; y=&quot;52&quot; width=&quot;32&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;51&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;the&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;69&quot; y=&quot;52&quot; width=&quot;32&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;85&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;dog&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;103&quot; y=&quot;52&quot; width=&quot;32&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;119&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;is&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;137&quot; y=&quot;52&quot; width=&quot;32&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;153&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;on&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;171&quot; y=&quot;52&quot; width=&quot;32&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;187&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;the&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;205&quot; y=&quot;52&quot; width=&quot;38&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;224&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;sand&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;245&quot; y=&quot;52&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;256&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;.&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;151&quot; y=&quot;86&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;text tokens only&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;35&quot; y=&quot;110&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;INFERENCE SEQUENCE&amp;lt;/text&amp;gt;
&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot;&amp;gt;
&amp;lt;rect x=&quot;35&quot; y=&quot;118&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;46&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; fill=&quot;#2563EB&quot;&amp;gt;v1&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;59&quot; y=&quot;118&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;70&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; fill=&quot;#2563EB&quot;&amp;gt;v2&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;83&quot; y=&quot;118&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;94&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; fill=&quot;#2563EB&quot;&amp;gt;v3&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;107&quot; y=&quot;118&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;118&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; fill=&quot;#2563EB&quot;&amp;gt;v4&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;133&quot; y=&quot;118&quot; width=&quot;38&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;152&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;what&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;173&quot; y=&quot;118&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;184&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;is&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;197&quot; y=&quot;118&quot; width=&quot;38&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;216&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;this&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;237&quot; y=&quot;118&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;248&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;?&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;line x1=&quot;80&quot; y1=&quot;155&quot; x2=&quot;80&quot; y2=&quot;140&quot; stroke=&quot;#B91C1C&quot; stroke-width=&quot;0.8&quot; stroke-dasharray=&quot;2,2&quot;/&amp;gt;
&amp;lt;text x=&quot;80&quot; y=&quot;166&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#B91C1C&quot;&amp;gt;vision (novel modality)&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;35&quot; y=&quot;190&quot; width=&quot;270&quot; height=&quot;40&quot; fill=&quot;#1A1A1A&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;210&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#F4EEE3&quot;&amp;gt;LLM (text-pretrained)&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;224&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#FEF3C7&quot;&amp;gt;+ vision adapter on top&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;170&quot; y1=&quot;170&quot; x2=&quot;170&quot; y2=&quot;188&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a4)&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;170&quot; y=&quot;252&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;vision tokens are out-of-distribution&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;266&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;relative to the LLM&apos;s pretraining,&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;280&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;arriving through a thin projector&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;340&quot; y1=&quot;35&quot; x2=&quot;340&quot; y2=&quot;295&quot; stroke=&quot;#C9BEAA&quot; stroke-dasharray=&quot;3,3&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;510&quot; y=&quot;20&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;13&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;Unified-decoder training&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;375&quot; y=&quot;44&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;PRETRAINING SEQUENCE&amp;lt;/text&amp;gt;
&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot;&amp;gt;
&amp;lt;rect x=&quot;375&quot; y=&quot;52&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;386&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; fill=&quot;#2563EB&quot;&amp;gt;v1&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;399&quot; y=&quot;52&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;410&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; fill=&quot;#2563EB&quot;&amp;gt;v2&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;423&quot; y=&quot;52&quot; width=&quot;32&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;439&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;the&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;457&quot; y=&quot;52&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#D1FAE5&quot; stroke=&quot;#15803D&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;468&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; fill=&quot;#15803D&quot;&amp;gt;a1&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;481&quot; y=&quot;52&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;492&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;is&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;505&quot; y=&quot;52&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;516&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; fill=&quot;#2563EB&quot;&amp;gt;v3&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;529&quot; y=&quot;52&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#D1FAE5&quot; stroke=&quot;#15803D&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;540&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; fill=&quot;#15803D&quot;&amp;gt;a2&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;553&quot; y=&quot;52&quot; width=&quot;38&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;572&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;sand&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;593&quot; y=&quot;52&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;604&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; fill=&quot;#2563EB&quot;&amp;gt;v4&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;617&quot; y=&quot;52&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#D1FAE5&quot; stroke=&quot;#15803D&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;628&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot; fill=&quot;#15803D&quot;&amp;gt;a3&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;507&quot; y=&quot;86&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;interleaved text + image + audio tokens&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;375&quot; y=&quot;110&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;INFERENCE SEQUENCE&amp;lt;/text&amp;gt;
&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot;&amp;gt;
&amp;lt;rect x=&quot;375&quot; y=&quot;118&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;386&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; fill=&quot;#2563EB&quot;&amp;gt;v1&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;399&quot; y=&quot;118&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;410&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; fill=&quot;#2563EB&quot;&amp;gt;v2&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;423&quot; y=&quot;118&quot; width=&quot;38&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;442&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;what&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;463&quot; y=&quot;118&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;474&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;is&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;487&quot; y=&quot;118&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;498&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;?&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;511&quot; y=&quot;118&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;522&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; fill=&quot;#2563EB&quot;&amp;gt;v3&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;535&quot; y=&quot;118&quot; width=&quot;22&quot; height=&quot;20&quot; fill=&quot;#D1FAE5&quot; stroke=&quot;#15803D&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&amp;lt;text x=&quot;546&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; fill=&quot;#15803D&quot;&amp;gt;a1&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;466&quot; y=&quot;166&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#15803D&quot;&amp;gt;same shape, in-distribution&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;466&quot; y1=&quot;156&quot; x2=&quot;466&quot; y2=&quot;140&quot; stroke=&quot;#15803D&quot; stroke-width=&quot;0.8&quot; stroke-dasharray=&quot;2,2&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;375&quot; y=&quot;190&quot; width=&quot;270&quot; height=&quot;40&quot; fill=&quot;#1A1A1A&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;510&quot; y=&quot;210&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#F4EEE3&quot;&amp;gt;LLM (trained on&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;510&quot; y=&quot;224&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#F4EEE3&quot;&amp;gt;multimodal sequences)&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;510&quot; y1=&quot;170&quot; x2=&quot;510&quot; y2=&quot;188&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a4)&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;510&quot; y=&quot;252&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;multimodal sequences flow through&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;510&quot; y=&quot;266&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;the LLM&apos;s full capacity during training&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;510&quot; y=&quot;280&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;(joint pretraining or staged SFT)&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 4.&amp;lt;/strong&amp;gt; Two views of what the LLM was exposed to multimodal data through. Adapter style (left) trains on text only; vision tokens show up at inference as a different shape from anything the LLM saw before, arriving through a thin projector. Unified-decoder style (right) trains the LLM on interleaved multimodal sequences, either as joint pretraining (Gemini) or as staged SFT on a text-pretrained backbone (Nemotron 3 Nano Omni). Either way, the LLM&apos;s full capacity sees multimodal data during training, not just at instruction-tune time.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;Two flavors of this exist, and the difference between them matters for what kinds of modalities are easy to add later.&lt;/p&gt;
&lt;p&gt;The first flavor is &lt;em&gt;discrete-token unified vocabulary&lt;/em&gt;, exemplified by Meta&apos;s &lt;a href=&quot;https://arxiv.org/abs/2405.09818&quot;&gt;Chameleon&lt;/a&gt;. Quantize images into discrete codes from a fixed codebook (Chameleon uses 8192 entries). Extend the text tokenizer&apos;s vocabulary to include those codes. Now an image is just a longer sequence of &quot;tokens&quot; drawn from a vocabulary that happens to be larger than a pure-text one. Train a standard autoregressive decoder on a mix of text-only sequences and interleaved text-and-image sequences, where the image segments are these new tokens. The model can read an image and write text, or read text and write image tokens, with no architectural difference between the two.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Our unified approach uses fully token-based representations for both image and textual modalities. By quantizing images into discrete tokens, analogous to words in text, we can apply the same transformer architecture to sequences of both image and text tokens, without the need for separate image/text encoders or domain-specific decoders.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The second flavor is &lt;em&gt;continuous projected embeddings&lt;/em&gt;. Instead of quantizing, run each modality through a modality-specific encoder that produces a sequence of continuous feature vectors, then project those vectors (via a learned MLP, the projector) into the LLM&apos;s hidden dimension, and concatenate them into the LLM&apos;s input sequence. The LLM still consumes a sequence of vectors, the same way it always does. The only difference is that some of those vectors came from a vision encoder or an audio encoder rather than from the text embedding table. Loss is computed on text outputs, but the gradient flows back through the LLM, the projectors, and (during the training stages where they&apos;re unfrozen) the encoders themselves, so all of the per-token text predictions are conditioned on the modality-projected positions appearing in the sequence.&lt;/p&gt;
&lt;p&gt;This is the family &lt;a href=&quot;https://arxiv.org/abs/2312.11805&quot;&gt;Gemini&lt;/a&gt;, &lt;a href=&quot;https://arxiv.org/abs/2503.20215&quot;&gt;Qwen2.5-Omni&lt;/a&gt;, and &lt;a href=&quot;https://research.nvidia.com/labs/nemotron/files/NVIDIA-Nemotron-3-Omni-report.pdf&quot;&gt;Nemotron 3 Nano Omni&lt;/a&gt; belong to. Gemini frames it as native-from-scratch:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;The Gemini models are natively multimodal, as they are trained jointly across text, image, audio, and video.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Nemotron 3 Nano Omni names the design pattern explicitly:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Our model follows an encoder-projector-decoder design, combining the Nemotron 3 Nano 30B-A3B language model with modality-specific encoders for vision and audio, connected via MLP projectors. ... Visual, audio, and text tokens are concatenated and fed to the LLM.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A subtle point worth nailing down. The &quot;tokens&quot; the LLM sees in this setup are not raw pixels or raw waveform samples. They are the &lt;em&gt;outputs&lt;/em&gt; of modality-specific encoders, projected by an MLP into the LLM&apos;s embedding dimension. So when we say the LLM consumes &quot;vision tokens,&quot; we mean vectors in the LLM&apos;s hidden space that happen to have come from a ViT plus a projector, not 256-dimensional image patches. The projector matters. The encoder matters. The LLM sees the same kind of vector regardless of where it came from, and that&apos;s what makes the unified-decoder approach work as a drop-in extension of transformer architecture.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://arxiv.org/abs/2502.13923&quot;&gt;Qwen2.5-VL&lt;/a&gt; fits here as a vision-only special case of the continuous-projected pattern. It uses an MLP-based vision-language merger (LLaVA-style spatial grouping plus a 2-layer MLP) and a redesigned ViT with windowed attention so that vision-encoder cost scales linearly with patch count. Qwen2.5-VL is a vision-language model, not a four-modality omni model, but architecturally it sits closer to Nemotron 3 Nano Omni&apos;s continuous-feature design than to LLaVA&apos;s frozen-LLM adapter.&lt;/p&gt;
&lt;p&gt;Qwen2.5-Omni adds the most subtle detail. Its speech-generation head, called Talker, doesn&apos;t read a transcript from the central decoder (&quot;Thinker&quot;). It reads Thinker&apos;s &lt;em&gt;hidden states&lt;/em&gt; directly:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Talker directly receives high-dimensional representations from Thinker and shares all of Thinker&apos;s historical context information. ... The high-dimensional representations provided by Thinker implicitly convey [content&apos;s tone and attitude before the entire text is fully generated].&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This matters for the next act. When the speech-out side of an agent loop reads hidden states instead of transcribed text, things like prosody and intent never go through a text bottleneck.&lt;/p&gt;
&lt;h3&gt;7. The tradeoff&lt;/h3&gt;
&lt;p&gt;Adapter style is the cheaper path. You inherit a pretrained text LLM, you inherit a pretrained vision encoder, and you train a small connector module against an instruction-tuning dataset. Total training compute is dominated by the connector training and the second-stage fine-tune; both are small compared to a from-scratch LLM pretraining run. You can swap LLMs without touching the vision side, and the work to add the modality is bounded.&lt;/p&gt;
&lt;p&gt;What you give up is depth of joint understanding. The LLM was trained on text. It learned how text sequences behave, how words constrain other words, how to follow instructions. It never learned what an image &lt;em&gt;looks like&lt;/em&gt; in the sense of having to predict or condition on one across the multimodal pretraining distribution. Whatever cross-modal inference shows up has to come through the projector. For tasks where the answer requires fluently weaving visual evidence into a text response, this can be enough. For tasks where the visual structure has to drive the reasoning (long video, multi-page documents, fine-grained audio cues), the projector ends up as a thin pipe.&lt;/p&gt;
&lt;p&gt;Unified-decoder style is more expensive. You pretrain (or substantially continue-train) a multi-billion-parameter LLM on multimodal sequences, with curated text+image, text+audio, and text+video data in the training mix and the cluster engineering to run it. You can&apos;t easily swap the LLM. You&apos;re committed to one stack.&lt;/p&gt;
&lt;p&gt;What you buy is depth. The LLM weights learn to expect multimodal input during pretraining. Reasoning that has to mix modalities (answer this question by reading the chart on page 4 of the PDF, or by listening to the speaker&apos;s tone in the third minute of the video) gets to use the LLM&apos;s full capacity, not just the capacity of a thin connector. And every additional modality you add (audio, video, robotics action sequences) plugs into the same input stream rather than adding a parallel adapter side-channel.&lt;/p&gt;
&lt;p&gt;Where Nemotron 3 Nano Omni sits is at the unified end of this dial. It is &lt;em&gt;not&lt;/em&gt; pretrained from scratch on multimodal data; the technical report describes a multi-stage post-training pipeline that takes the existing Nemotron 3 Nano 30B-A3B base LLM and runs it through seven SFT stages plus an RL phase, several of which unfreeze all parameters and train them jointly with the encoders and projectors over mixed-modality sequences. The HF model card&apos;s modality rollup gives a sense of scale:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;354,587,705 data points (~717.0B tokens) ... text+audio: 259,178,821 samples (~143,533.1M tokens); text+image: 70,143,901 samples (~180,347.1M tokens); text+video: 15,837,673 samples (~239,631.5M tokens); text+video+audio: 8,720,044 samples (~152,499.2M tokens); text: 707,187 samples (~958.4M tokens).&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The 30B-A3B backbone itself was pretrained as a text-first model; the omni capabilities arrive through staged mixed-modality post-training rather than through a from-scratch native-multimodal pretraining run (Gemini&apos;s framing). What&apos;s load-bearing is the shape of the mix: text+image, text+audio, and text+video sequences all show up with the LLM in the loss, across many billions of tokens.&lt;/p&gt;
&lt;p&gt;Compare to an adapter-style model where a small instruction-tuning dataset sits on top of a frozen text-pretrained LLM. The two regimes train on different distributions. Adapter models work well when the multimodal signal the LLM needs to use is something a thin projector can encode and the LLM&apos;s text-only weights can interpret as token-shaped input. They tend to struggle when the input drifts further out of distribution from anything the LLM saw during pretraining (long video, dense documents, audio whose information lives in prosody more than wording), because the projector becomes a bottleneck on what the LLM can possibly attend to. Unified-decoder models carry that distribution into the LLM&apos;s training (whether as native pretraining or as staged SFT), so the bottleneck moves.&lt;/p&gt;
&lt;h2&gt;Act 3: the other senses&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Why audio and video aren&apos;t more SigLIPs.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;8. Audio is time-frequency, not pixel grid&lt;/h3&gt;
&lt;p&gt;The first thing to notice about audio is that the natural input format is not 1D and not pixel-shaped. A microphone produces a time series of pressure values (a waveform), but modern audio models almost always convert the waveform into a 2D representation, called a log-mel spectrogram, before any neural network touches it.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 260&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; style=&quot;height: auto&quot;&amp;gt;
&amp;lt;defs&amp;gt;&amp;lt;marker id=&quot;a5&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;5&quot; markerHeight=&quot;5&quot; orient=&quot;auto&quot;&amp;gt;&amp;lt;path d=&quot;M0,0 L10,5 L0,10 z&quot; fill=&quot;#6B6258&quot;/&amp;gt;&amp;lt;/marker&amp;gt;&amp;lt;/defs&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;150&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;RAW WAVEFORM&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;40&quot; width=&quot;240&quot; height=&quot;160&quot; fill=&quot;#F9F5EB&quot; stroke=&quot;#C9BEAA&quot;/&amp;gt;
&amp;lt;line x1=&quot;30&quot; y1=&quot;120&quot; x2=&quot;270&quot; y2=&quot;120&quot; stroke=&quot;#C9BEAA&quot; stroke-width=&quot;0.5&quot; stroke-dasharray=&quot;2,2&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;polyline points=&quot;30,120 34,121 38,119 42,121 46,119 50,124 54,112 58,140 62,98 66,148 70,92 74,148 78,95 82,145 86,98 90,140 94,105 98,130 102,115 106,125 110,118 114,128 118,110 122,145 126,92 130,148 134,90 138,145 142,92 146,143 150,98 154,140 158,108 162,132 166,115 170,130 174,110 178,132 182,110 186,130 190,112 194,128 198,115 202,125 206,118 210,124 214,119 218,122 222,120 226,121 230,120 234,120 238,120 242,120 246,120 250,120 254,120 258,120 262,120 266,120 270,120&quot; fill=&quot;none&quot; stroke=&quot;#1A1A1A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;24&quot; y=&quot;44&quot; text-anchor=&quot;end&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;+1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;24&quot; y=&quot;124&quot; text-anchor=&quot;end&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;0&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;24&quot; y=&quot;204&quot; text-anchor=&quot;end&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;-1&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;30&quot; y=&quot;216&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;0&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;150&quot; y=&quot;216&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;0.5 s&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;270&quot; y=&quot;216&quot; text-anchor=&quot;end&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;1 s&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;150&quot; y=&quot;240&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;16,000 samples / second&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;282&quot; y1=&quot;120&quot; x2=&quot;368&quot; y2=&quot;120&quot; stroke=&quot;#6B6258&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#a5)&quot;/&amp;gt;
&amp;lt;text x=&quot;325&quot; y=&quot;105&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#1A1A1A&quot;&amp;gt;25 ms window,&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;325&quot; y=&quot;118&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#1A1A1A&quot;&amp;gt;10 ms stride,&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;325&quot; y=&quot;139&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#1A1A1A&quot;&amp;gt;80 mel bins, log&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;500&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;LOG-MEL SPECTROGRAM&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;380&quot; y=&quot;40&quot; width=&quot;240&quot; height=&quot;160&quot; fill=&quot;#F4EEE3&quot; stroke=&quot;#8F8578&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;380&quot; y=&quot;160&quot; width=&quot;240&quot; height=&quot;40&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.30&quot;/&amp;gt;
&amp;lt;rect x=&quot;380&quot; y=&quot;130&quot; width=&quot;240&quot; height=&quot;30&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.18&quot;/&amp;gt;
&amp;lt;rect x=&quot;380&quot; y=&quot;100&quot; width=&quot;240&quot; height=&quot;30&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.08&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;399&quot; y=&quot;60&quot; width=&quot;51&quot; height=&quot;140&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.20&quot;/&amp;gt;
&amp;lt;rect x=&quot;471&quot; y=&quot;80&quot; width=&quot;29&quot; height=&quot;120&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.24&quot;/&amp;gt;
&amp;lt;rect x=&quot;507&quot; y=&quot;65&quot; width=&quot;43&quot; height=&quot;135&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.20&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;399&quot; y=&quot;120&quot; width=&quot;51&quot; height=&quot;40&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.22&quot;/&amp;gt;
&amp;lt;rect x=&quot;507&quot; y=&quot;115&quot; width=&quot;43&quot; height=&quot;45&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.26&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;399&quot; y=&quot;50&quot; width=&quot;5&quot; height=&quot;42&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.55&quot;/&amp;gt;
&amp;lt;rect x=&quot;471&quot; y=&quot;55&quot; width=&quot;5&quot; height=&quot;32&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.55&quot;/&amp;gt;
&amp;lt;rect x=&quot;507&quot; y=&quot;50&quot; width=&quot;5&quot; height=&quot;40&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.55&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;382&quot; y=&quot;44&quot; width=&quot;34&quot; height=&quot;13&quot; fill=&quot;#F4EEE3&quot; opacity=&quot;0.85&quot;/&amp;gt;
&amp;lt;text x=&quot;385&quot; y=&quot;54&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;7 kHz&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;382&quot; y=&quot;186&quot; width=&quot;34&quot; height=&quot;13&quot; fill=&quot;#F4EEE3&quot; opacity=&quot;0.85&quot;/&amp;gt;
&amp;lt;text x=&quot;385&quot; y=&quot;196&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;0 Hz&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;380&quot; y=&quot;216&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;0&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;500&quot; y=&quot;216&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;0.5 s&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;620&quot; y=&quot;216&quot; text-anchor=&quot;end&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;1 s&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;500&quot; y=&quot;240&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;100 time frames × 80 mel bins&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 5.&amp;lt;/strong&amp;gt; A 1-second utterance and the log-mel tile every modern audio model turns it into. Left: the microphone signal, sampled 16,000 times per second. Right: what comes out after a sliding 25 ms window, an 80-bin mel filterbank, and a per-bin log. Rows are mel-spaced frequency channels (low at the bottom, high at the top); columns are 10 ms time frames. Time means the same thing on both axes; the vertical axis on the right has no spatial locality.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;A spectrogram is what you get when you take a sliding window of, say, 25 milliseconds across the waveform, and for each window compute the energy of the signal at each of a fixed set of frequency bins. The &quot;mel&quot; part means the frequency bins are spaced on the perceptual mel scale rather than linearly, because human auditory perception is approximately log-frequency. The &quot;log&quot; means you take the log of the energy in each bin, because perception is approximately log-amplitude. You end up with a 2D matrix whose rows are frequency bins (typically 80 of them) and whose columns are time frames (typically one every 10 milliseconds).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://arxiv.org/abs/2212.04356&quot;&gt;Whisper&lt;/a&gt; uses exactly this representation, and it has become the de facto convention:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;All audio is re-sampled to 16,000 Hz, and an 80-channel log-magnitude Mel spectrogram representation is computed on 25-millisecond windows with a stride of 10 milliseconds.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So a 30-second audio clip becomes an 80×3000 matrix. A Conv1D stem then downsamples 2× along the time axis and projects the 80 mel bins into the model hidden dimension, producing a 1500-step sequence of model-dimension vectors that gets fed into a Transformer. The frequency axis is folded into channels at the stem rather than carried through as 80 separate rows; what survives downstream is a sequence-of-vectors view of the audio.&lt;/p&gt;
&lt;p&gt;Here is why this matters for the omni-modal architecture. A vision encoder like C-RADIOv4 takes a 2D pixel grid and produces a sequence of patch tokens. The 2D grid has spatial locality: nearby patches depict the same object, lighting, texture. The encoder&apos;s inductive biases (patch tokenization, 2D rotary embeddings, sometimes window attention) are tuned for that.&lt;/p&gt;
&lt;p&gt;A spectrogram is also a 2D grid, but the two axes mean different things. The horizontal axis is time, with strong sequential structure: what happens at second 10 is causally tied to what happened at second 9. The vertical axis is frequency channel, with no spatial locality at all: bin 5 (a low-frequency band, maybe 100 Hz) and bin 6 (slightly higher, maybe 150 Hz) describe related parts of the signal, but bin 5 and bin 78 describe completely different aspects of the same signal at the same time.&lt;/p&gt;
&lt;p&gt;A vision ViT applied naively to a spectrogram would treat both axes the same and would miss the structure on both. You want temporal smoothing along the time axis (which is what attention plus convolutional downsampling buys) and you want every frequency bin to be visible to every step of the encoder, not split across patches. Audio encoders therefore use Conformer-style architectures, which interleave local convolutions with attention, instead of plain ViTs; they project the frequency axis into model channels at the stem rather than tokenizing it spatially, and they downsample aggressively along time.&lt;/p&gt;
&lt;p&gt;C-RADIOv4 cannot just be reused for audio. The inductive biases are wrong. You need a different encoder built for this representation.&lt;/p&gt;
&lt;h3&gt;9. Parakeet: a token-emitting audio encoder&lt;/h3&gt;
&lt;p&gt;Nemotron 3 Nano Omni&apos;s audio encoder is &lt;a href=&quot;https://huggingface.co/nvidia/parakeet-tdt-0.6b-v2&quot;&gt;Parakeet-TDT-0.6B-v2&lt;/a&gt;, an NVIDIA model that pairs a FastConformer encoder with a Token-and-Duration Transducer (TDT) decoder. 600 million parameters total. Its model card describes the shape:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Architecture Type: FastConformer-TDT ... This model was developed based on FastConformer encoder architecture and TDT decoder. This model has 600 million model parameters.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In its original ASR setting (automatic speech recognition), Parakeet ingests a log-mel spectrogram and emits a transcript. The encoder is the part that produces a sequence of audio feature vectors, downsampled 8× along the time axis from the spectrogram input. The TDT decoder is what turns those features into characters. Together they&apos;re a full speech-to-text model.&lt;/p&gt;
&lt;p&gt;In the omni-modal setting, the TDT decoder is thrown away. Nemotron uses only Parakeet&apos;s encoder, treating it as a feature extractor whose outputs pass through a 2-layer MLP projector and get concatenated into the LLM&apos;s input sequence:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;The audio side is powered by Parakeet-TDT-0.6B-v2, connected to the backbone through its own 2-layer MLP projector. Audio is sampled at 16 kHz, and the model is trained with inputs up to 1,200 seconds (20 minutes), while the LLM max context length supports 5+ hours.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 280&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; style=&quot;height: auto&quot;&amp;gt;
&amp;lt;defs&amp;gt;&amp;lt;marker id=&quot;a6&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;5&quot; markerHeight=&quot;5&quot; orient=&quot;auto&quot;&amp;gt;&amp;lt;path d=&quot;M0,0 L10,5 L0,10 z&quot; fill=&quot;#6B6258&quot;/&amp;gt;&amp;lt;/marker&amp;gt;&amp;lt;/defs&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;50&quot; y=&quot;32&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;INPUT&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;20&quot; y=&quot;110&quot; width=&quot;60&quot; height=&quot;60&quot; fill=&quot;#F4EEE3&quot; stroke=&quot;#8F8578&quot;/&amp;gt;
&amp;lt;rect x=&quot;20&quot; y=&quot;155&quot; width=&quot;60&quot; height=&quot;15&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.32&quot;/&amp;gt;
&amp;lt;rect x=&quot;22&quot; y=&quot;120&quot; width=&quot;14&quot; height=&quot;32&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.22&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;125&quot; width=&quot;12&quot; height=&quot;28&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.20&quot;/&amp;gt;
&amp;lt;rect x=&quot;56&quot; y=&quot;118&quot; width=&quot;14&quot; height=&quot;38&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.24&quot;/&amp;gt;
&amp;lt;rect x=&quot;22&quot; y=&quot;113&quot; width=&quot;3&quot; height=&quot;18&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.5&quot;/&amp;gt;
&amp;lt;rect x=&quot;56&quot; y=&quot;115&quot; width=&quot;3&quot; height=&quot;14&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.5&quot;/&amp;gt;
&amp;lt;text x=&quot;50&quot; y=&quot;186&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;log-mel&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;50&quot; y=&quot;200&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;80×100&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;85&quot; y1=&quot;140&quot; x2=&quot;103&quot; y2=&quot;140&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a6)&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;155&quot; y=&quot;86&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;SHARED ENCODER&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;105&quot; y=&quot;95&quot; width=&quot;100&quot; height=&quot;90&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;155&quot; y=&quot;118&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#2563EB&quot;&amp;gt;FastConformer&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;155&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#2563EB&quot;&amp;gt;encoder&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;155&quot; y=&quot;151&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;600M params&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;155&quot; y=&quot;164&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;8× temporal&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;155&quot; y=&quot;176&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;subsample&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;208&quot; y1=&quot;140&quot; x2=&quot;226&quot; y2=&quot;140&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a6)&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot;&amp;gt;
&amp;lt;rect x=&quot;230&quot; y=&quot;125&quot; width=&quot;14&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;246&quot; y=&quot;125&quot; width=&quot;14&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;262&quot; y=&quot;125&quot; width=&quot;14&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;278&quot; y=&quot;125&quot; width=&quot;14&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;294&quot; y=&quot;125&quot; width=&quot;14&quot; height=&quot;20&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;270&quot; y=&quot;160&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;~12.5 features/sec&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;375&quot; y=&quot;40&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot; opacity=&quot;0.55&quot;&amp;gt;DISCARDED IN OMNI-MODAL&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g opacity=&quot;0.5&quot;&amp;gt;
&amp;lt;path d=&quot;M 270 122 Q 280 90 305 75 L 320 75&quot; fill=&quot;none&quot; stroke=&quot;#6B6258&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;3,3&quot; marker-end=&quot;url(#a6)&quot;/&amp;gt;
&amp;lt;rect x=&quot;325&quot; y=&quot;55&quot; width=&quot;100&quot; height=&quot;44&quot; fill=&quot;#EDE5D4&quot; stroke=&quot;#8F8578&quot; stroke-width=&quot;1.2&quot; stroke-dasharray=&quot;4,3&quot;/&amp;gt;
&amp;lt;text x=&quot;375&quot; y=&quot;75&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; fill=&quot;#6B6258&quot;&amp;gt;TDT decoder&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;375&quot; y=&quot;91&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;(thrown away)&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;430&quot; y1=&quot;77&quot; x2=&quot;448&quot; y2=&quot;77&quot; stroke=&quot;#6B6258&quot; stroke-dasharray=&quot;3,3&quot; marker-end=&quot;url(#a6)&quot;/&amp;gt;
&amp;lt;rect x=&quot;450&quot; y=&quot;62&quot; width=&quot;84&quot; height=&quot;30&quot; fill=&quot;#F9F5EB&quot; stroke=&quot;#8F8578&quot; stroke-dasharray=&quot;3,2&quot;/&amp;gt;
&amp;lt;text x=&quot;492&quot; y=&quot;82&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot;&amp;gt;&quot;hello...&quot;&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;544&quot; y=&quot;80&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;(original ASR path)&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;path d=&quot;M 270 148 Q 280 180 305 195 L 320 195&quot; fill=&quot;none&quot; stroke=&quot;#6B6258&quot; stroke-width=&quot;1.2&quot; marker-end=&quot;url(#a6)&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;325&quot; y=&quot;180&quot; width=&quot;100&quot; height=&quot;40&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;375&quot; y=&quot;198&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#B8651A&quot;&amp;gt;2-layer MLP&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;375&quot; y=&quot;212&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;→ LLM hidden dim&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;430&quot; y1=&quot;200&quot; x2=&quot;448&quot; y2=&quot;200&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a6)&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;450&quot; y=&quot;180&quot; width=&quot;200&quot; height=&quot;40&quot; fill=&quot;#1A1A1A&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;550&quot; y=&quot;198&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#F4EEE3&quot;&amp;gt;LLM input sequence&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;550&quot; y=&quot;212&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#FEF3C7&quot;&amp;gt;audio + text + image tokens&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;375&quot; y=&quot;240&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;OMNI-MODAL HEAD&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 6.&amp;lt;/strong&amp;gt; What Nemotron actually keeps from Parakeet. The original Parakeet-TDT speech-recognition model is a FastConformer encoder feeding a Token-and-Duration Transducer that writes out a transcript. In omni-modal use, only the encoder runs; the TDT decoder is discarded, and the encoder&apos;s audio features (8× temporal subsampling of a 100 Hz log-mel spectrogram, so the encoder emits ~12.5 features per second of audio) pass through a 2-layer MLP projector into the LLM&apos;s input sequence instead.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;This is the architectural counterpart of what happens on the vision side. C-RADIOv4-H is a vision encoder; its outputs flow through a projector into the LLM. Parakeet&apos;s encoder is an audio encoder; its outputs flow through a projector into the same LLM. Two MLPs, two encoders, one decoder.&lt;/p&gt;
&lt;p&gt;The choice to use Parakeet&apos;s encoder rather than Whisper&apos;s encoder is itself worth a moment. Whisper is the more famous open speech model, and either encoder produces a log-mel-conditioned feature sequence that an LLM can in principle consume. The technical report and model card don&apos;t justify the choice in detail, so this post won&apos;t either; the architectural pattern (encoder plus projector into LLM) is what matters, and Parakeet versus Whisper sits below that as an implementation detail.&lt;/p&gt;
&lt;h3&gt;10. The cost of stitched chains&lt;/h3&gt;
&lt;p&gt;The alternative to using Parakeet&apos;s encoder this way is the older &quot;stitched chain&quot; pattern: run Whisper end-to-end (encoder plus decoder), produce a transcript, feed the transcript text to the LLM. This was the dominant approach for any system needing to handle audio before native multimodal LLMs landed, and it still appears in production stacks today.&lt;/p&gt;
&lt;p&gt;It has three problems, in increasing order of how subtle they are.&lt;/p&gt;
&lt;p&gt;First, repetition loops. Whisper&apos;s own paper documents the failure mode in §3.8:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Whisper models are trained on 30-second audio chunks and cannot consume longer audio inputs at once... Transcribing long-form audio using Whisper relies on accurate prediction of the timestamp tokens to determine the amount to shift the model&apos;s 30-second audio context window by, and inaccurate transcription in one window may negatively impact transcription in the subsequent windows. We have developed a set of heuristics that help avoid failure cases of long-form transcription... we use beam search with 5 beams using the log probability as the score function, to reduce repetition looping which happens more frequently in greedy decoding.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The OpenAI authors are saying, plainly, that long-form Whisper occasionally falls into repetition loops in greedy decoding and needs hand-tuned heuristics to avoid window-to-window error compounding. When the LLM downstream consumes the transcript, those errors land in its context as if they were ground truth. There is no posterior signal to back out from.&lt;/p&gt;
&lt;p&gt;Second, lost paralinguistic information. Speech carries content beyond the words: prosody, stress, intonation, speaker identity, disfluency, overlap. A transcript collapses all of it to a string. &lt;a href=&quot;https://arxiv.org/abs/2410.24019&quot;&gt;Tsiamas et al.&lt;/a&gt; ran a controlled benchmark of cascade vs end-to-end speech translation systems and found that cascades transmit prosody to a &lt;em&gt;lesser extent&lt;/em&gt; than end-to-end systems, with the gap depending on what surface form the transcriber chooses:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;The prosody of a spoken utterance, including features like stress, intonation and rhythm, can significantly affect the underlying semantics, and as a consequence can also affect its textual translation.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A canonical example from their paper: &quot;These are GERMAN teachers&quot; (teachers from Germany) and &quot;These are German TEACHERS&quot; (teachers of German) are different statements that map to the same transcript if the transcriber doesn&apos;t capture the stress. The cascade has no way to recover what was lost; the end-to-end architecture can in principle preserve it.&lt;/p&gt;
&lt;p&gt;Third, latency hop. Whisper has to finish a chunk before the LLM sees anything from that chunk. The architecture forces an extra serial step: any cascade with an autoregressive ASR head must commit to a transcript before the downstream model sees the input. There is no controlled benchmark for this in the agent-loop literature, so the claim stays qualitative; what&apos;s load-bearing is that the cascade introduces a latency hop the unified design doesn&apos;t, not any specific number.&lt;/p&gt;
&lt;p&gt;Qwen2.5-Omni&apos;s Talker is the architectural answer in the &lt;em&gt;other&lt;/em&gt; direction. Instead of generating speech via a text-to-speech head that reads a transcript from the LLM, Talker reads the LLM&apos;s hidden states directly:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Talker directly receives high-dimensional representations from Thinker and shares all of Thinker&apos;s historical context information. ... The high-dimensional representations provided by Thinker implicitly convey [content&apos;s tone and attitude before the entire text is fully generated].&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The point is the same as on the input side. Any time speech information has to round-trip through a text bottleneck, you lose what couldn&apos;t be written down. Native multimodal models avoid the round-trip by sharing hidden states across the modality boundary.&lt;/p&gt;
&lt;p&gt;That covers audio. Video has its own problem, and it&apos;s a bigger one.&lt;/p&gt;
&lt;h3&gt;11. Video is spatiotemporal redundancy&lt;/h3&gt;
&lt;p&gt;A 2D image with 16×16 patches at 384×384 resolution is around 576 tokens. A modest number; an LLM with a 32k context window has room for it. Now stretch the same model to video.&lt;/p&gt;
&lt;p&gt;A video is a sequence of frames over time. The naive thing to do is treat each frame as an image, run the ViT, and concatenate the resulting tokens into one long sequence. The math, even at modest settings, is bad. The &lt;a href=&quot;https://arxiv.org/abs/2510.14624&quot;&gt;EVS paper&lt;/a&gt; opens with the cleanest version of this problem statement:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;a two-minute video at 24 FPS produces more than two million vision tokens, far beyond the effective context length of most language models&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;Figure caption=&quot;Vision-token counts under three encoding strategies, against the 262K context window of Nemotron 3 Nano Omni. The naive per-frame ViT overflows the window for almost any non-trivial clip. Conv3D tubelet encoding halves the count by fusing pairs of frames before the ViT, and EVS at q = 0.75 then keeps only the top quartile of dynamic post-projection tokens. The product is what makes long video viable inside one decoder context. Nemotron&apos;s production recipe also applies a 4× pixel-shuffle reduction prior to projection (see Fig 9); this figure isolates the two compression ideas the post is teaching, and treats the patch-to-token mapping as 1:1 to keep the comparison honest as you drag the sliders.&quot; figNum={7}&amp;gt;
&amp;lt;VideoTokenExplosion client:visible /&amp;gt;
&amp;lt;/Figure&amp;gt;&lt;/p&gt;
&lt;p&gt;Drag the duration slider above. Even at modest patch counts, the token total flies past a 262K-context LLM the moment the clip gets past about ten seconds. At 1024 patches per frame and 30 FPS, you&apos;re already over 262K at nine seconds. That&apos;s the cost ViViT-style naive frame-by-frame encoding pays, and it makes long-form video reasoning impossible without aggressive compression.&lt;/p&gt;
&lt;p&gt;The good news, made vivid by &lt;a href=&quot;https://arxiv.org/abs/2203.12602&quot;&gt;VideoMAE&lt;/a&gt; in 2022, is that video is wildly redundant along the time axis. VideoMAE found that you can mask out 90 to 95 percent of the patches in a video and still reconstruct it, where the equivalent ratio for static images is around 75 percent.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;VideoMAE is in favor of extremely high masking ratios (e.g. 90% to 95%) compared with the ImageMAE.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The intuition is that two consecutive frames usually share most of their content. Static parts of a scene (the background, the unchanging foreground object, the camera angle) all contribute redundant patches at every frame. You don&apos;t need to encode them N times. You need to encode the parts that &lt;em&gt;change&lt;/em&gt;, plus enough of the static structure to anchor where they change against.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://arxiv.org/abs/2103.15691&quot;&gt;ViViT&lt;/a&gt; (Google, 2021) was the first widely-used architecture to bake this into the encoder. Instead of tokenizing each frame independently, ViViT extracts non-overlapping spatio-temporal &quot;tubes&quot; that span both spatial extent and a small number of consecutive frames:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Extract non-overlapping, spatio-temporal &apos;tubes&apos; from the input volume, and to linearly project this to ℝ^d ... nt=⌊T/t⌋, nh=⌊H/h⌋ and nw=⌊W/w⌋, tokens are extracted from the temporal, height, and width dimensions respectively.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A single token now represents, say, a 2×16×16 chunk of video (two consecutive frames worth of one spatial patch). Tubelet tokenization halves the token count along the time axis at the input stage. Variants exist (TimeSformer&apos;s factorized space-time attention, MViT&apos;s pyramidal pooling) but the underlying observation is the same: time-axis redundancy is so high that you can collapse along it without losing the signal that matters.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://arxiv.org/abs/2102.05095&quot;&gt;TimeSformer&lt;/a&gt; makes the cost story explicit:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;In practice, joint space-time attention causes a GPU memory overflow once the spatial frame resolution reaches 448 pixels, or once the number of frames is increased to 32.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can&apos;t stack a vanilla ViT and run full self-attention on a video; the (NF)² cost dominates the kernel. You either factorize attention (do spatial attention within frames, then temporal attention across frames) or you compress at the input (tubelets). Or both.&lt;/p&gt;
&lt;p&gt;Tubelet encoding gets you a 2× reduction. Good but not enough to make a 2-minute video fit in a 262K-context LLM. You need another order of magnitude. That&apos;s what the next section is about.&lt;/p&gt;
&lt;h3&gt;12. Efficient Video Sampling&lt;/h3&gt;
&lt;p&gt;The trick that closes the gap is straightforward. If patches at the same spatial location in consecutive frames are nearly identical (because nothing in the scene changed there), drop the later one. Keep only patches where something changed. This is what the EVS paper proposes.&lt;/p&gt;
&lt;p&gt;The intuition is easiest with raw pixels. Take a frame at time &lt;em&gt;t&lt;/em&gt; and a frame at time &lt;em&gt;t−1&lt;/em&gt;. For each spatial patch position &lt;em&gt;p&lt;/em&gt;, compute the L1 distance between the two patches:&lt;/p&gt;
&lt;p&gt;$$
D_{p,t} = |p_t - p_{t-1}|_1
$$&lt;/p&gt;
&lt;p&gt;You now have a 2D map of &quot;how much did this patch change between these two frames.&quot; Collect these distances across the whole clip, pick the &lt;em&gt;q&lt;/em&gt;-th percentile, and use that as a threshold. Drop every patch whose distance is below the threshold:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;For every patch $p$ at time $0 &amp;lt; t \leq T$, compute $D_{p,t} = |p_t - p_{t-1}|&lt;em&gt;1$, and denote ${D_t}$ as the differences of all patches between frames $t-1$ and $t$.&quot;
&quot;For each frame collect ${D_t}&lt;/em&gt;{t=1}^T$ and compute sequence-level cut-off threshold $d$ as $q$-th percentile, where $q$ is a user-selected pruning rate.&quot;
&quot;For all patches in the consecutive frames, keep those that satisfy $D_{p,t} \geq d$; this defines the binary mask $M_t$.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That&apos;s the algorithm. &lt;em&gt;q&lt;/em&gt; is user-tunable: q=0 keeps everything; q=0.75 keeps the top 25% of changing patches; q=0.95 keeps almost nothing. The first frame is always kept whole, since there&apos;s no &lt;em&gt;t−1&lt;/em&gt; to diff against.&lt;/p&gt;
&lt;p&gt;&amp;lt;Figure caption=&quot;EVS pruning on a synthetic two-frame scene. The left frame (t − 1) is always kept whole, since there is no preceding frame to diff against. In the right frame (t), patches whose L1 difference from the matching patch at t − 1 falls below the q-th percentile are greyed out. As q rises, only the patches around the moving object survive; the static background is dropped because earlier frames already represent it. The figure uses raw RGB difference for visual intuition; production EVS computes the same threshold on encoded features.&quot; figNum={8}&amp;gt;
&amp;lt;EvsPatchPruning client:visible /&amp;gt;
&amp;lt;/Figure&amp;gt;&lt;/p&gt;
&lt;p&gt;Drag the &lt;em&gt;q&lt;/em&gt; slider. Notice what survives. The patches that get kept are the parts of the scene that are actually changing: the moving objects, the camera-relative motion, the cut transitions. The patches that get dropped are the static background already represented in earlier frames. The intuition is that most of what you drop is redundancy. The cost is real but small; the EVS authors report &quot;up to 4×&quot; speedups with &quot;minimal accuracy loss,&quot; and on Video-MME at &lt;em&gt;q&lt;/em&gt;=0.75 with uptraining the score drops by about 0.80 points (65.50 → 64.70 on 32-frame; see §12). Pruning is not lossless, just cheap relative to the compute it saves.&lt;/p&gt;
&lt;p&gt;There&apos;s a wrinkle. Naively dropping tokens can disturb how downstream position embeddings interpret the resulting sequence, since they expect a contiguous progression of token indices. EVS&apos;s authors describe their algorithm as preserving positional identity:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;EVS preserves positional identity, requires no architectural changes or retraining.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The implementation detail varies. The EVS paper&apos;s framing is that surviving patches retain their original spatial-temporal indices, so position embeddings stay faithful. Nemotron 3 Nano Omni&apos;s public code applies a boolean retention mask to the post-projection token sequence (&quot;inputs_embeds&quot; in the HF model), which is a related but more pedestrian implementation: the kept feature vectors are concatenated and fed to the LLM in their original order, without re-indexing or re-positioning. Either way, no architectural change to the LLM is needed and the post-prune sequence stays a drop-in replacement for the original.&lt;/p&gt;
&lt;p&gt;A separate EVS-paper detail is optional uptraining. The EVS authors sample &lt;em&gt;q&lt;/em&gt; from a beta distribution during mini-batches so an uptrained model can tolerate a continuum of compression ratios:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;the pruning rate q is sampled from a beta distribution for every mini-batch. The model thus learns to be invariant to a continuum of compression ratios&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;NVIDIA&apos;s Nemotron report does not claim this recipe for Nano Omni; it describes EVS as a runtime-only feature and evaluates &lt;em&gt;q&lt;/em&gt; at inference (Table 12 fixes &lt;em&gt;q&lt;/em&gt;=0.5, Table 13 sweeps &lt;em&gt;q&lt;/em&gt;). So for Nemotron, treat &lt;em&gt;q&lt;/em&gt; as an inference knob the report benchmarks, not as a compression level the omni checkpoint necessarily saw during training.&lt;/p&gt;
&lt;p&gt;Second, the production deployment runs EVS in &lt;em&gt;embedding space&lt;/em&gt;, not raw RGB. The simple RGB intuition above is exactly that, an intuition. The EVS paper itself runs and uptrains the algorithm on encoded feature similarity rather than raw pixels. In Nemotron 3 Nano Omni&apos;s specific implementation, the report and the public modeling code place EVS &lt;em&gt;post-adapter&lt;/em&gt;. Pruning operates on the video token embeddings after Conv3D tubelet, pixel shuffle, and the MLP projector, immediately before the LLM. Raw RGB L1 over-weights pixel differences that are visually salient but semantically unimportant (lighting drift, sensor noise, mild camera jitter) and under-weights changes that are small in pixels but large in meaning (a face turning, a hand crossing into frame). Computing the threshold on encoded features fixes both directions because the encoder has already discounted nuisance variation. The algorithm structure is the same; only the input space changes.&lt;/p&gt;
&lt;p&gt;Reported results, kept separate because they measure different things. The EVS paper reports a general &quot;up to 4× LLM TTFT speedup with minimal accuracy loss,&quot; varying by model and &lt;em&gt;q&lt;/em&gt;. Their Video-MME table at &lt;em&gt;q&lt;/em&gt;=0.75 with uptraining shows an absolute 0.80-point drop on the 32-frame setting (65.50 → 64.70, equivalent to about 1.2% relative). The two numbers measure different things; don&apos;t bundle them into &quot;4× at &lt;em&gt;q&lt;/em&gt;=0.75.&quot;&lt;/p&gt;
&lt;p&gt;EVS plus the tubelet encoding from §11 is what makes long video viable in a 262K-context LLM. Nemotron composes them in a specific order.&lt;/p&gt;
&lt;h3&gt;13. How Nemotron stacks the two&lt;/h3&gt;
&lt;p&gt;Nemotron 3 Nano Omni&apos;s video path runs the reductions in a specific order. First, frames are fused into tubelets at the input layer of the vision encoder (the ViViT idea). The vision encoder runs over the tubelets and emits patch features; those features go through the 4× pixel-shuffle reduction and the MLP projector to land in the LLM&apos;s hidden dimension. EVS then prunes the post-projection token sequence at inference time on encoded-feature similarity, immediately before the LLM:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;For video, Nemotron 3 Nano Omni uses a dedicated Conv3D tubelet embedding path. Instead of embedding each frame independently, every pair of consecutive frames is fused into a single &apos;tubelet&apos; before the ViT, halving the number of vision tokens the language model has to attend to.&quot;
&quot;EVS is an important feature, enabled during inference time, that drops redundant video tokens after the vision encoder. ... The first frame of the video is kept entirely, then for each subsequent frame, EVS keeps the &apos;dynamic&apos; tokens where the video is changing and drops the &apos;static&apos; ones where nothing has changed from the previous frame.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 240&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; style=&quot;height: auto&quot;&amp;gt;
&amp;lt;defs&amp;gt;&amp;lt;marker id=&quot;a9&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;5&quot; markerHeight=&quot;5&quot; orient=&quot;auto&quot;&amp;gt;&amp;lt;path d=&quot;M0,0 L10,5 L0,10 z&quot; fill=&quot;#6B6258&quot;/&amp;gt;&amp;lt;/marker&amp;gt;&amp;lt;/defs&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;340&quot; y=&quot;20&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;VIDEO TOKEN BUDGET (30 S @ 24 FPS, 1024 PATCHES/FRAME)&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;20&quot; y=&quot;55&quot; width=&quot;55&quot; height=&quot;40&quot; fill=&quot;#F4EEE3&quot; stroke=&quot;#8F8578&quot;/&amp;gt;
&amp;lt;rect x=&quot;25&quot; y=&quot;60&quot; width=&quot;55&quot; height=&quot;40&quot; fill=&quot;#F4EEE3&quot; stroke=&quot;#8F8578&quot;/&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;65&quot; width=&quot;55&quot; height=&quot;40&quot; fill=&quot;#EDE5D4&quot; stroke=&quot;#8F8578&quot;/&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;65&quot; width=&quot;55&quot; height=&quot;14&quot; fill=&quot;#93C5FD&quot; opacity=&quot;0.6&quot;/&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;79&quot; width=&quot;55&quot; height=&quot;12&quot; fill=&quot;#D1AE7A&quot; opacity=&quot;0.7&quot;/&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;91&quot; width=&quot;55&quot; height=&quot;14&quot; fill=&quot;#059669&quot; opacity=&quot;0.5&quot;/&amp;gt;
&amp;lt;text x=&quot;55&quot; y=&quot;125&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;raw frames&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;55&quot; y=&quot;143&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;737,280&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;55&quot; y=&quot;158&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;naive baseline&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;98&quot; y1=&quot;80&quot; x2=&quot;123&quot; y2=&quot;80&quot; stroke=&quot;#6B6258&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#a9)&quot;/&amp;gt;
&amp;lt;text x=&quot;111&quot; y=&quot;71&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; font-weight=&quot;600&quot; fill=&quot;#B8651A&quot;&amp;gt;×0.5&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;128&quot; y=&quot;50&quot; width=&quot;90&quot; height=&quot;70&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;173&quot; y=&quot;73&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#B8651A&quot;&amp;gt;Conv3D&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;173&quot; y=&quot;87&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#B8651A&quot;&amp;gt;tubelet&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;173&quot; y=&quot;105&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;2 frames → 1 token&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;173&quot; y=&quot;143&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;368,640&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;173&quot; y=&quot;158&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;after time fusion&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;221&quot; y1=&quot;80&quot; x2=&quot;246&quot; y2=&quot;80&quot; stroke=&quot;#6B6258&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#a9)&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;251&quot; y=&quot;50&quot; width=&quot;90&quot; height=&quot;70&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;296&quot; y=&quot;73&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#2563EB&quot;&amp;gt;ViT&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;296&quot; y=&quot;87&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#2563EB&quot;&amp;gt;C-RADIOv4-H&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;296&quot; y=&quot;105&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;vision encoder&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;296&quot; y=&quot;143&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;368,640&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;296&quot; y=&quot;158&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;passes through&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;344&quot; y1=&quot;80&quot; x2=&quot;369&quot; y2=&quot;80&quot; stroke=&quot;#6B6258&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#a9)&quot;/&amp;gt;
&amp;lt;text x=&quot;357&quot; y=&quot;71&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; font-weight=&quot;600&quot; fill=&quot;#B8651A&quot;&amp;gt;×0.125&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;374&quot; y=&quot;50&quot; width=&quot;124&quot; height=&quot;70&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;436&quot; y=&quot;73&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#B8651A&quot;&amp;gt;pixel-shuffle ×4&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;436&quot; y=&quot;87&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#B8651A&quot;&amp;gt;+ EVS @ q=0.5&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;436&quot; y=&quot;105&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;prune redundant 50%&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;436&quot; y=&quot;143&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#15803D&quot;&amp;gt;46,080&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;436&quot; y=&quot;158&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#15803D&quot;&amp;gt;16× total reduction&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;501&quot; y1=&quot;80&quot; x2=&quot;526&quot; y2=&quot;80&quot; stroke=&quot;#6B6258&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#a9)&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;531&quot; y=&quot;50&quot; width=&quot;129&quot; height=&quot;70&quot; fill=&quot;#1A1A1A&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;595&quot; y=&quot;74&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#F4EEE3&quot;&amp;gt;LLM&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;595&quot; y=&quot;90&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#FEF3C7&quot;&amp;gt;Nemotron 30B-A3B&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;595&quot; y=&quot;106&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#FEF3C7&quot;&amp;gt;262,144-token context&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;595&quot; y=&quot;143&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#15803D&quot;&amp;gt;fits&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;595&quot; y=&quot;158&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;18% of context budget&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;40&quot; y=&quot;190&quot; width=&quot;500&quot; height=&quot;14&quot; fill=&quot;#EDE5D4&quot; stroke=&quot;#8F8578&quot; stroke-width=&quot;0.5&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;190&quot; width=&quot;250&quot; height=&quot;14&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.45&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;190&quot; width=&quot;31&quot; height=&quot;14&quot; fill=&quot;#15803D&quot; opacity=&quot;0.7&quot;/&amp;gt;
&amp;lt;line x1=&quot;178&quot; y1=&quot;190&quot; x2=&quot;178&quot; y2=&quot;204&quot; stroke=&quot;#1A1A1A&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;2,2&quot;/&amp;gt;
&amp;lt;text x=&quot;178&quot; y=&quot;216&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;8&quot; fill=&quot;#6B6258&quot;&amp;gt;262K context&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;555&quot; y=&quot;200&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;8&quot; fill=&quot;#6B6258&quot;&amp;gt;naive 737K&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;555&quot; y=&quot;211&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;8&quot; fill=&quot;#B8651A&quot;&amp;gt;+ tubelet 369K&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;555&quot; y=&quot;222&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;8&quot; fill=&quot;#15803D&quot;&amp;gt;+ shuffle + EVS 46K&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 9.&amp;lt;/strong&amp;gt; The three compressions Nemotron stacks for video. Conv3D tubelet fusion at the input layer halves the token count along time (Nemotron 3 Nano Omni report, §2). The vision encoder runs over the fused tubelets and emits patch features. The Nemotron pipeline then applies a 4× pixel-shuffle reduction prior to projection (&quot;Prior to projection, we apply a pixel shuffle operation with 4× downsampling to reduce the token count presented to the language model,&quot; §2). EVS is an optional inference-time pass that sits between the vision adapter and the LLM, dropping post-projection tokens that didn&apos;t change much from the previous frame; the report&apos;s published inference-efficiency setup runs EVS at q=0.5. Cascade total is ×0.5 × ×0.25 × ×0.5 = ×0.0625: a 30-second clip whose naive token count is 737,280 lands at 46,080 by the time it hits the LLM, well inside the 262,144-token context window. The cascade reaches ~23K if you crank EVS to q=0.75, but that isn&apos;t the Nemotron-deployed setting.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;Three compressions compose multiplicatively in Nemotron&apos;s actual pipeline. Tubelet halves the token count along time. A 4× pixel-shuffle reduction sits between the vision encoder and the projector, dropping the per-frame post-encoder count by another factor of four. EVS, the optional inference-time pruner, sits between the vision adapter and the LLM and drops post-projection tokens that didn&apos;t change much from the previous frame; the report&apos;s published inference-efficiency setup runs EVS at &lt;em&gt;q&lt;/em&gt;=0.5, so it removes about half of what&apos;s left. Together you&apos;re at roughly 1/16 of the naive frame-by-frame token total. For an illustrative 30-second clip at 24 FPS and 1024 patches per frame, the naive count is 30 × 24 × 1024 = 737,280 tokens. After tubelet fusion (×0.5), pixel-shuffle (×0.25), and EVS at &lt;em&gt;q&lt;/em&gt;=0.5 (×0.5), you&apos;re at roughly 46,000 tokens. (Production deployments don&apos;t sample at 24 FPS in any case; the HF serving guidance recommends ~2 FPS / 256 frames as a default. The 24-FPS budget is a worst-case stress test for the compression chain, not the default ingestion path.) Crank EVS to &lt;em&gt;q&lt;/em&gt;=0.75 (the EVS paper&apos;s headline setting) and the same 30-second clip lands at ~23K tokens; the report doesn&apos;t ship that as the default but the runtime knob is there.&lt;/p&gt;
&lt;p&gt;For images (single frames, no temporal axis), the pipeline simplifies. Tubelet fusion isn&apos;t applicable; only the spatial ViT runs. The patch count varies with resolution:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Each image can be represented using a variable number of 16 x 16 patches, with a minimum of 1,024 to a maximum of 13,312 visual patches per image.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A 13,312-patch image at 16-pixel patches is roughly 3.4 megapixels of input area (13,312 × 256 = 3,407,872 pixels), enough resolution for document understanding or fine-grained chart reading. The 1,024-patch lower bound (about 0.26 MP) is for thumbnail-sized images where dense detail isn&apos;t relevant.&lt;/p&gt;
&lt;h2&gt;Act 4: the inference math&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;The Nemotron-specific efficiency receipts, and what they actually show.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There&apos;s no controlled benchmark at the primary-source bar that proves &quot;unified-decoder beats fragmented chains&quot; in general. What we &lt;em&gt;do&lt;/em&gt; have are three measured efficiency wins on specific models against specific baselines, plus the architectural reasons each one shows up. The post reports the numbers, names the baselines, and stops there.&lt;/p&gt;
&lt;h3&gt;14. The hybrid backbone&lt;/h3&gt;
&lt;p&gt;Nemotron 3 Nano Omni&apos;s language model is roughly 30 billion parameters total but only activates a few billion of them per input token. The omni model card rounds these as 31B / ~3B active; the underlying Nemotron 3 Nano backbone card the omni inherits from is more precise about the architecture, and that&apos;s where the layer counts and routing details below come from:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;The model employs a hybrid Mixture-of-Experts (MoE) architecture, consisting of 23 Mamba-2 and MoE layers, along with 6 Attention layers. Each MoE layer includes 128 experts plus 1 shared expert, with 6 experts activated per token. The model has 3.5B active parameters and 30B parameters in total.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Three things are happening here. The backbone is a hybrid of three layer types: Mamba-2 selective state-space layers, grouped-query attention layers, and Mixture-of-Experts feedforward layers. It uses sparse activation: of the 128 experts in each MoE layer, only the 6 the router picks (plus a shared expert that&apos;s always on) get computed for each token. And the layer counts are uneven: 23 Mamba-2 + 6 attention + 23 MoE = 52 total layers, with attention only ~11.5% of the stack.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 290&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; style=&quot;height: auto&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;340&quot; y=&quot;20&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;52 LAYERS, INPUT TO OUTPUT (ORDERING ILLUSTRATIVE)&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;0.4&quot;&amp;gt;
&amp;lt;rect x=&quot;20&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;44&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;68&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;92&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;128&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;152&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;176&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;200&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;236&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;260&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;284&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;308&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;344&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;368&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;392&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;416&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;452&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;476&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;500&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;524&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;560&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;584&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;608&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g fill=&quot;#D1FAE5&quot; stroke=&quot;#15803D&quot; stroke-width=&quot;0.4&quot;&amp;gt;
&amp;lt;rect x=&quot;32&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;56&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;80&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;104&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;140&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;164&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;188&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;212&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;248&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;272&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;296&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;320&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;356&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;380&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;404&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;428&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;464&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;488&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;512&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;536&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;572&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;596&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;620&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot;&amp;gt;
&amp;lt;rect x=&quot;116&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;224&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;332&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;440&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;548&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;632&quot; y=&quot;40&quot; width=&quot;12&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;14&quot; y=&quot;68&quot; text-anchor=&quot;end&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;in&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;666&quot; y=&quot;68&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;out&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;8&quot; fill=&quot;#6B6258&quot;&amp;gt;
&amp;lt;text x=&quot;26&quot; y=&quot;105&quot; text-anchor=&quot;middle&quot;&amp;gt;1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;134&quot; y=&quot;105&quot; text-anchor=&quot;middle&quot;&amp;gt;10&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;254&quot; y=&quot;105&quot; text-anchor=&quot;middle&quot;&amp;gt;20&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;374&quot; y=&quot;105&quot; text-anchor=&quot;middle&quot;&amp;gt;30&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;494&quot; y=&quot;105&quot; text-anchor=&quot;middle&quot;&amp;gt;40&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;614&quot; y=&quot;105&quot; text-anchor=&quot;middle&quot;&amp;gt;50&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;638&quot; y=&quot;105&quot; text-anchor=&quot;middle&quot;&amp;gt;52&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;20&quot; y=&quot;130&quot; width=&quot;200&quot; height=&quot;92&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;28&quot; y=&quot;150&quot; font-family=&quot;serif&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot; fill=&quot;#2563EB&quot;&amp;gt;Mamba-2 ×23&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;28&quot; y=&quot;170&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#1A1A1A&quot;&amp;gt;selective state-space,&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;28&quot; y=&quot;183&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#1A1A1A&quot;&amp;gt;O(N) cost in sequence&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;28&quot; y=&quot;196&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#1A1A1A&quot;&amp;gt;length, no attention&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;240&quot; y=&quot;130&quot; width=&quot;200&quot; height=&quot;92&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;248&quot; y=&quot;150&quot; font-family=&quot;serif&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot; fill=&quot;#B8651A&quot;&amp;gt;GQA attention ×6&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;248&quot; y=&quot;170&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#1A1A1A&quot;&amp;gt;~11.5% of the stack;&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;248&quot; y=&quot;183&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#1A1A1A&quot;&amp;gt;kept for the kind of&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;248&quot; y=&quot;196&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#1A1A1A&quot;&amp;gt;exact-position lookup&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;248&quot; y=&quot;207&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#1A1A1A&quot;&amp;gt;attention is good at&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;460&quot; y=&quot;130&quot; width=&quot;200&quot; height=&quot;92&quot; fill=&quot;#D1FAE5&quot; stroke=&quot;#15803D&quot; stroke-width=&quot;1.5&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;text x=&quot;468&quot; y=&quot;150&quot; font-family=&quot;serif&quot; font-size=&quot;12&quot; font-weight=&quot;600&quot; fill=&quot;#15803D&quot;&amp;gt;MoE feedforward ×23&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;468&quot; y=&quot;170&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#1A1A1A&quot;&amp;gt;128 routed experts,&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;468&quot; y=&quot;183&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#1A1A1A&quot;&amp;gt;top-6 active per token,&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;468&quot; y=&quot;196&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#1A1A1A&quot;&amp;gt;+ 1 always-on shared expert&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;20&quot; y1=&quot;232&quot; x2=&quot;660&quot; y2=&quot;232&quot; stroke=&quot;#C9BEAA&quot;/&amp;gt;
&amp;lt;text x=&quot;20&quot; y=&quot;250&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;30B parameters total&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;250&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#6B6258&quot;&amp;gt;→&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;660&quot; y=&quot;250&quot; text-anchor=&quot;end&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#15803D&quot;&amp;gt;3.5B active per token&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;270&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;model card states the counts and the routing top-k, not the within-stack ordering of the attention layers&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 10.&amp;lt;/strong&amp;gt; The 52-layer hybrid backbone. Most of the stack alternates Mamba-2 (blue) selective state-space layers with MoE (green) feedforward layers; six grouped-query attention layers (rust) sit interspersed at roughly even spacing. Where each attention layer specifically falls is illustrative, not authoritative; the model card pins the counts and the routing but not the placement.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;The model card states the counts and the routing top-k. It does not state the exact within-stack ordering of attention layers. NVIDIA&apos;s earlier Nemotron-H paper places attention layers &quot;evenly dispersed throughout the model,&quot; but Nemotron-H is a different model and a literal copy of that arrangement isn&apos;t quoted for the omni model anywhere I could find. The figure draws an evenly dispersed layout because that&apos;s a reasonable default and matches the family pattern. The placement is illustrative, not authoritative.&lt;/p&gt;
&lt;p&gt;A note worth pinning down: the routing is &lt;em&gt;per token&lt;/em&gt;, not per modality. The same router sees text tokens, vision tokens, audio tokens (all of them, after projection, are vectors in the LLM&apos;s hidden space), and selects 6 experts for each one based on the token&apos;s content. Experts can and do specialize on patterns that show up disproportionately in one modality, but that specialization is learned, not architecturally enforced. There is no &quot;vision gate&quot; or &quot;audio gate.&quot; The marketing material occasionally suggests there is. The published primary sources don&apos;t.&lt;/p&gt;
&lt;h3&gt;15. Why Mamba&lt;/h3&gt;
&lt;p&gt;The reason a hybrid uses Mamba layers at all is that long-context prefill is what kills attention.&lt;/p&gt;
&lt;p&gt;Self-attention has prefill cost proportional to sequence length squared (every token attends to every other). For a 1K-token context, this doesn&apos;t matter; for a 256K-token context, it dominates time-to-first-token. Mamba is what you get when you keep the residual-stream architecture of a transformer but replace the attention layer with a &lt;em&gt;selective state-space model&lt;/em&gt;, which processes the same context in cost proportional to sequence length. Linear, not quadratic. (Decode-step cost with a KV cache is a different story for both, scaling with the cached length per generated token; the prefill gap is what dominates 262K-context inference.)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Letting the SSM parameters be functions of the input addresses their weakness with discrete modalities, allowing the model to selectively propagate or forget information.&quot;
&quot;Mamba enjoys fast inference (5× higher throughput than Transformers) and linear scaling in sequence length.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What &quot;selective&quot; means is that the state-update equations have parameters that depend on the current input token, instead of being shared across all positions. This is what lets Mamba pick what to remember and what to drop, with the input deciding rather than a fixed weight. The trade-off is that the recurrence is no longer the kind of linear time-invariant system you can compute with a single FFT-based convolution; you need a hardware-aware scan kernel to keep training fast.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://arxiv.org/abs/2405.21060&quot;&gt;Mamba-2&lt;/a&gt; (Dao and Gu, 2024) shows that this scan and a restricted form of attention turn out to be the same matrix-multiplication structure under the hood, which the authors call structured state-space duality. Mamba-2&apos;s core layer is 2-8× faster than the original Mamba&apos;s:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Our state space duality (SSD) framework allows us to design a new architecture (Mamba-2) whose core layer is a refinement of Mamba&apos;s selective SSM that is 2-8X faster, while continuing to be competitive with Transformers.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 280&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; style=&quot;height: auto&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;340&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;RELATIVE COMPUTE COST vs CONTEXT LENGTH (log–log)&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;80&quot; y1=&quot;50&quot; x2=&quot;80&quot; y2=&quot;220&quot; stroke=&quot;#8F8578&quot;/&amp;gt;
&amp;lt;line x1=&quot;80&quot; y1=&quot;220&quot; x2=&quot;640&quot; y2=&quot;220&quot; stroke=&quot;#8F8578&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g stroke=&quot;#C9BEAA&quot; stroke-width=&quot;0.5&quot; stroke-dasharray=&quot;2,3&quot;&amp;gt;
&amp;lt;line x1=&quot;80&quot; y1=&quot;106.7&quot; x2=&quot;640&quot; y2=&quot;106.7&quot;/&amp;gt;
&amp;lt;line x1=&quot;80&quot; y1=&quot;163.3&quot; x2=&quot;640&quot; y2=&quot;163.3&quot;/&amp;gt;
&amp;lt;line x1=&quot;267&quot; y1=&quot;50&quot; x2=&quot;267&quot; y2=&quot;220&quot;/&amp;gt;
&amp;lt;line x1=&quot;453&quot; y1=&quot;50&quot; x2=&quot;453&quot; y2=&quot;220&quot;/&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot;&amp;gt;
&amp;lt;text x=&quot;74&quot; y=&quot;223&quot; text-anchor=&quot;end&quot;&amp;gt;1×&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;74&quot; y=&quot;166&quot; text-anchor=&quot;end&quot;&amp;gt;100×&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;74&quot; y=&quot;110&quot; text-anchor=&quot;end&quot;&amp;gt;10K×&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;74&quot; y=&quot;53&quot; text-anchor=&quot;end&quot;&amp;gt;1M×&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;80&quot; y=&quot;237&quot; text-anchor=&quot;middle&quot;&amp;gt;1K&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;267&quot; y=&quot;237&quot; text-anchor=&quot;middle&quot;&amp;gt;10K&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;453&quot; y=&quot;237&quot; text-anchor=&quot;middle&quot;&amp;gt;100K&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;640&quot; y=&quot;237&quot; text-anchor=&quot;middle&quot;&amp;gt;1M&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;360&quot; y=&quot;258&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; fill=&quot;#6B6258&quot;&amp;gt;context length (tokens)&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;80&quot; y=&quot;42&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;cost (×1K-token baseline)&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;polyline points=&quot;80,220 136,212 211,200 267,192 305,186 361,177 396,172 453,163 510,155 531,152 584,144 640,135&quot; fill=&quot;none&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;2.5&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;polyline points=&quot;80,220 136,203 211,180 267,163 305,152 361,135 396,124 453,107 510,90 531,83 584,67 640,50&quot; fill=&quot;none&quot; stroke=&quot;#B91C1C&quot; stroke-width=&quot;2.5&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g stroke=&quot;#1A1A1A&quot; stroke-width=&quot;0.6&quot; stroke-dasharray=&quot;3,2&quot;&amp;gt;
&amp;lt;line x1=&quot;305&quot; y1=&quot;50&quot; x2=&quot;305&quot; y2=&quot;220&quot;/&amp;gt;
&amp;lt;line x1=&quot;396&quot; y1=&quot;50&quot; x2=&quot;396&quot; y2=&quot;220&quot;/&amp;gt;
&amp;lt;line x1=&quot;531&quot; y1=&quot;50&quot; x2=&quot;531&quot; y2=&quot;220&quot;/&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;
&amp;lt;text x=&quot;305&quot; y=&quot;42&quot; text-anchor=&quot;middle&quot;&amp;gt;16K&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;396&quot; y=&quot;42&quot; text-anchor=&quot;middle&quot;&amp;gt;49K&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;531&quot; y=&quot;42&quot; text-anchor=&quot;middle&quot;&amp;gt;262K&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;555&quot; y=&quot;170&quot; font-family=&quot;serif&quot; font-size=&quot;13&quot; font-style=&quot;italic&quot; fill=&quot;#2563EB&quot;&amp;gt;Mamba&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;555&quot; y=&quot;184&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#2563EB&quot;&amp;gt;O(N)&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;555&quot; y=&quot;100&quot; font-family=&quot;serif&quot; font-size=&quot;13&quot; font-style=&quot;italic&quot; fill=&quot;#B91C1C&quot;&amp;gt;Transformer&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;555&quot; y=&quot;113&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#B91C1C&quot;&amp;gt;O(N²)&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 11.&amp;lt;/strong&amp;gt; Prefill cost (full-context processing) scales linearly with sequence length for Mamba&apos;s selective state-space layer, and quadratically for self-attention. The two curves are indistinguishable at 1K tokens. At 16K, attention&apos;s full-context pass is already 16× more expensive than Mamba&apos;s; at 262K the gap is 262×. This is the ratio that makes long-context prefill affordable. Decode-step cost with a KV cache scales differently and is not what this figure measures.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;At 262K, pure-attention prefill is unaffordable; this is what Mamba buys.&lt;/p&gt;
&lt;p&gt;Why not pure Mamba? Because pure-Mamba models suffer on certain in-context retrieval tasks where attention&apos;s exact-position lookups matter. NVIDIA&apos;s &lt;a href=&quot;https://arxiv.org/abs/2504.03624&quot;&gt;Nemotron-H&lt;/a&gt; paper, which has a similar hybrid layout, says it directly:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Roughly 8% of the total layers in the model are self-attention layers; these layers are evenly dispersed throughout the model.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Nemotron 3 Nano Omni is at ~11.5% attention, slightly higher than Nemotron-H&apos;s 8%, consistent with NVIDIA&apos;s framing about preserving stronger global interaction in the omni model. Either way, the principle is the same: keep enough attention layers to handle the retrieval-flavored tasks attention is good at, and replace the rest with Mamba to make long-context inference affordable.&lt;/p&gt;
&lt;p&gt;The headline number from Nemotron-H, the closest primary source for the family&apos;s hybrid efficiency claims:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Nemotron-H-56B-Base can generate 2.4× more output tokens per second per GPU compared to Qwen-2.5-72B and Llama-3.1-70B.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A separate measurement on long contexts: the Nemotron-H research page reports a 2.9× speedup for the smaller Nemotron-H-47B at 64K input, 1024 output tokens on H100, again versus Qwen-2.5-72B and Llama-3.1-70B. The same architectural choice carries forward into Nemotron 3 Nano Omni, where the language backbone is a smaller hybrid sized for omni-modal serving rather than monolingual chat.&lt;/p&gt;
&lt;h3&gt;16. MoE: total parameters versus active parameters&lt;/h3&gt;
&lt;p&gt;The other half of the &quot;30B total / 3.5B active&quot; math comes from the MoE layers. The idea is older than Mamba and goes back at least to &lt;a href=&quot;https://arxiv.org/abs/2101.03961&quot;&gt;Switch Transformer&lt;/a&gt; in 2021. Each token gets routed to a small subset of experts; the rest sit out:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;We simplify the MoE routing algorithm and design intuitive improved models with reduced communication and computational costs.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The high-level pitch is that you can grow the parameter count by adding experts without growing the per-token compute, because each token only sees a fixed number of them. Switch routes each token to exactly one expert (top-1). &lt;a href=&quot;https://arxiv.org/abs/2401.04088&quot;&gt;Mixtral 8x7B&lt;/a&gt; in 2024 generalized to top-2 across 8 experts, popularizing the &quot;total params vs active params&quot; naming convention:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;For every token, at each layer, a router network selects two experts to process the current state and combine their outputs.&quot;
&quot;Each token has access to 47B parameters, but only uses 13B active parameters during inference.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Nemotron 3 Nano scales this up to 128 experts per MoE layer with top-6 routing, plus a shared expert that runs for every token regardless of routing.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 320&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; style=&quot;height: auto&quot;&amp;gt;
&amp;lt;defs&amp;gt;
&amp;lt;marker id=&quot;a12&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;5&quot; markerHeight=&quot;5&quot; orient=&quot;auto&quot;&amp;gt;&amp;lt;path d=&quot;M0,0 L10,5 L0,10 z&quot; fill=&quot;#6B6258&quot;/&amp;gt;&amp;lt;/marker&amp;gt;
&amp;lt;marker id=&quot;a12r&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;5&quot; markerHeight=&quot;5&quot; orient=&quot;auto&quot;&amp;gt;&amp;lt;path d=&quot;M0,0 L10,5 L0,10 z&quot; fill=&quot;#B8651A&quot;/&amp;gt;&amp;lt;/marker&amp;gt;
&amp;lt;marker id=&quot;a12g&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;5&quot; markerHeight=&quot;5&quot; orient=&quot;auto&quot;&amp;gt;&amp;lt;path d=&quot;M0,0 L10,5 L0,10 z&quot; fill=&quot;#15803D&quot;/&amp;gt;&amp;lt;/marker&amp;gt;
&amp;lt;/defs&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;290&quot; y=&quot;20&quot; width=&quot;100&quot; height=&quot;22&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot;/&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;36&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#1A1A1A&quot;&amp;gt;input token&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;340&quot; y1=&quot;44&quot; x2=&quot;340&quot; y2=&quot;58&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a12)&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;120&quot; y=&quot;60&quot; width=&quot;440&quot; height=&quot;40&quot; fill=&quot;#1A1A1A&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;80&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#F4EEE3&quot;&amp;gt;Router (128-way softmax)&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;93&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#FEF3C7&quot;&amp;gt;scores expert i for this token&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g stroke=&quot;#B8651A&quot; stroke-width=&quot;0.6&quot; stroke-dasharray=&quot;2,2&quot; opacity=&quot;0.7&quot;&amp;gt;
&amp;lt;line x1=&quot;70&quot; y1=&quot;100&quot; x2=&quot;70&quot; y2=&quot;123&quot;/&amp;gt;
&amp;lt;line x1=&quot;130&quot; y1=&quot;100&quot; x2=&quot;130&quot; y2=&quot;128&quot;/&amp;gt;
&amp;lt;line x1=&quot;226&quot; y1=&quot;100&quot; x2=&quot;226&quot; y2=&quot;122&quot;/&amp;gt;
&amp;lt;line x1=&quot;318&quot; y1=&quot;100&quot; x2=&quot;318&quot; y2=&quot;130&quot;/&amp;gt;
&amp;lt;line x1=&quot;402&quot; y1=&quot;100&quot; x2=&quot;402&quot; y2=&quot;126&quot;/&amp;gt;
&amp;lt;line x1=&quot;486&quot; y1=&quot;100&quot; x2=&quot;486&quot; y2=&quot;132&quot;/&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g fill=&quot;#D1AE7A&quot; opacity=&quot;0.65&quot;&amp;gt;
&amp;lt;rect x=&quot;20&quot; y=&quot;170&quot; width=&quot;4&quot; height=&quot;10&quot;/&amp;gt;
&amp;lt;rect x=&quot;24&quot; y=&quot;166&quot; width=&quot;4&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;28&quot; y=&quot;172&quot; width=&quot;4&quot; height=&quot;8&quot;/&amp;gt;
&amp;lt;rect x=&quot;32&quot; y=&quot;164&quot; width=&quot;4&quot; height=&quot;16&quot;/&amp;gt;
&amp;lt;rect x=&quot;36&quot; y=&quot;168&quot; width=&quot;4&quot; height=&quot;12&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;162&quot; width=&quot;4&quot; height=&quot;18&quot;/&amp;gt;
&amp;lt;rect x=&quot;44&quot; y=&quot;174&quot; width=&quot;4&quot; height=&quot;6&quot;/&amp;gt;
&amp;lt;rect x=&quot;48&quot; y=&quot;167&quot; width=&quot;4&quot; height=&quot;13&quot;/&amp;gt;
&amp;lt;rect x=&quot;52&quot; y=&quot;169&quot; width=&quot;4&quot; height=&quot;11&quot;/&amp;gt;
&amp;lt;rect x=&quot;56&quot; y=&quot;165&quot; width=&quot;4&quot; height=&quot;15&quot;/&amp;gt;
&amp;lt;rect x=&quot;60&quot; y=&quot;171&quot; width=&quot;4&quot; height=&quot;9&quot;/&amp;gt;
&amp;lt;rect x=&quot;64&quot; y=&quot;163&quot; width=&quot;4&quot; height=&quot;17&quot;/&amp;gt;
&amp;lt;rect x=&quot;72&quot; y=&quot;161&quot; width=&quot;4&quot; height=&quot;19&quot;/&amp;gt;
&amp;lt;rect x=&quot;76&quot; y=&quot;170&quot; width=&quot;4&quot; height=&quot;10&quot;/&amp;gt;
&amp;lt;rect x=&quot;80&quot; y=&quot;166&quot; width=&quot;4&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;84&quot; y=&quot;172&quot; width=&quot;4&quot; height=&quot;8&quot;/&amp;gt;
&amp;lt;rect x=&quot;88&quot; y=&quot;164&quot; width=&quot;4&quot; height=&quot;16&quot;/&amp;gt;
&amp;lt;rect x=&quot;92&quot; y=&quot;168&quot; width=&quot;4&quot; height=&quot;12&quot;/&amp;gt;
&amp;lt;rect x=&quot;96&quot; y=&quot;162&quot; width=&quot;4&quot; height=&quot;18&quot;/&amp;gt;
&amp;lt;rect x=&quot;100&quot; y=&quot;174&quot; width=&quot;4&quot; height=&quot;6&quot;/&amp;gt;
&amp;lt;rect x=&quot;104&quot; y=&quot;167&quot; width=&quot;4&quot; height=&quot;13&quot;/&amp;gt;
&amp;lt;rect x=&quot;108&quot; y=&quot;169&quot; width=&quot;4&quot; height=&quot;11&quot;/&amp;gt;
&amp;lt;rect x=&quot;112&quot; y=&quot;165&quot; width=&quot;4&quot; height=&quot;15&quot;/&amp;gt;
&amp;lt;rect x=&quot;116&quot; y=&quot;171&quot; width=&quot;4&quot; height=&quot;9&quot;/&amp;gt;
&amp;lt;rect x=&quot;120&quot; y=&quot;163&quot; width=&quot;4&quot; height=&quot;17&quot;/&amp;gt;
&amp;lt;rect x=&quot;124&quot; y=&quot;173&quot; width=&quot;4&quot; height=&quot;7&quot;/&amp;gt;
&amp;lt;rect x=&quot;132&quot; y=&quot;170&quot; width=&quot;4&quot; height=&quot;10&quot;/&amp;gt;
&amp;lt;rect x=&quot;136&quot; y=&quot;166&quot; width=&quot;4&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;140&quot; y=&quot;172&quot; width=&quot;4&quot; height=&quot;8&quot;/&amp;gt;
&amp;lt;rect x=&quot;144&quot; y=&quot;164&quot; width=&quot;4&quot; height=&quot;16&quot;/&amp;gt;
&amp;lt;rect x=&quot;148&quot; y=&quot;168&quot; width=&quot;4&quot; height=&quot;12&quot;/&amp;gt;
&amp;lt;rect x=&quot;152&quot; y=&quot;162&quot; width=&quot;4&quot; height=&quot;18&quot;/&amp;gt;
&amp;lt;rect x=&quot;156&quot; y=&quot;174&quot; width=&quot;4&quot; height=&quot;6&quot;/&amp;gt;
&amp;lt;rect x=&quot;160&quot; y=&quot;167&quot; width=&quot;4&quot; height=&quot;13&quot;/&amp;gt;
&amp;lt;rect x=&quot;164&quot; y=&quot;169&quot; width=&quot;4&quot; height=&quot;11&quot;/&amp;gt;
&amp;lt;rect x=&quot;168&quot; y=&quot;165&quot; width=&quot;4&quot; height=&quot;15&quot;/&amp;gt;
&amp;lt;rect x=&quot;172&quot; y=&quot;171&quot; width=&quot;4&quot; height=&quot;9&quot;/&amp;gt;
&amp;lt;rect x=&quot;176&quot; y=&quot;163&quot; width=&quot;4&quot; height=&quot;17&quot;/&amp;gt;
&amp;lt;rect x=&quot;180&quot; y=&quot;173&quot; width=&quot;4&quot; height=&quot;7&quot;/&amp;gt;
&amp;lt;rect x=&quot;184&quot; y=&quot;161&quot; width=&quot;4&quot; height=&quot;19&quot;/&amp;gt;
&amp;lt;rect x=&quot;188&quot; y=&quot;170&quot; width=&quot;4&quot; height=&quot;10&quot;/&amp;gt;
&amp;lt;rect x=&quot;192&quot; y=&quot;166&quot; width=&quot;4&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;196&quot; y=&quot;172&quot; width=&quot;4&quot; height=&quot;8&quot;/&amp;gt;
&amp;lt;rect x=&quot;200&quot; y=&quot;164&quot; width=&quot;4&quot; height=&quot;16&quot;/&amp;gt;
&amp;lt;rect x=&quot;204&quot; y=&quot;168&quot; width=&quot;4&quot; height=&quot;12&quot;/&amp;gt;
&amp;lt;rect x=&quot;208&quot; y=&quot;162&quot; width=&quot;4&quot; height=&quot;18&quot;/&amp;gt;
&amp;lt;rect x=&quot;212&quot; y=&quot;174&quot; width=&quot;4&quot; height=&quot;6&quot;/&amp;gt;
&amp;lt;rect x=&quot;216&quot; y=&quot;167&quot; width=&quot;4&quot; height=&quot;13&quot;/&amp;gt;
&amp;lt;rect x=&quot;220&quot; y=&quot;169&quot; width=&quot;4&quot; height=&quot;11&quot;/&amp;gt;
&amp;lt;rect x=&quot;228&quot; y=&quot;171&quot; width=&quot;4&quot; height=&quot;9&quot;/&amp;gt;
&amp;lt;rect x=&quot;232&quot; y=&quot;163&quot; width=&quot;4&quot; height=&quot;17&quot;/&amp;gt;
&amp;lt;rect x=&quot;236&quot; y=&quot;173&quot; width=&quot;4&quot; height=&quot;7&quot;/&amp;gt;
&amp;lt;rect x=&quot;240&quot; y=&quot;161&quot; width=&quot;4&quot; height=&quot;19&quot;/&amp;gt;
&amp;lt;rect x=&quot;244&quot; y=&quot;170&quot; width=&quot;4&quot; height=&quot;10&quot;/&amp;gt;
&amp;lt;rect x=&quot;248&quot; y=&quot;166&quot; width=&quot;4&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;252&quot; y=&quot;172&quot; width=&quot;4&quot; height=&quot;8&quot;/&amp;gt;
&amp;lt;rect x=&quot;256&quot; y=&quot;164&quot; width=&quot;4&quot; height=&quot;16&quot;/&amp;gt;
&amp;lt;rect x=&quot;260&quot; y=&quot;168&quot; width=&quot;4&quot; height=&quot;12&quot;/&amp;gt;
&amp;lt;rect x=&quot;264&quot; y=&quot;162&quot; width=&quot;4&quot; height=&quot;18&quot;/&amp;gt;
&amp;lt;rect x=&quot;268&quot; y=&quot;174&quot; width=&quot;4&quot; height=&quot;6&quot;/&amp;gt;
&amp;lt;rect x=&quot;272&quot; y=&quot;167&quot; width=&quot;4&quot; height=&quot;13&quot;/&amp;gt;
&amp;lt;rect x=&quot;276&quot; y=&quot;169&quot; width=&quot;4&quot; height=&quot;11&quot;/&amp;gt;
&amp;lt;rect x=&quot;280&quot; y=&quot;165&quot; width=&quot;4&quot; height=&quot;15&quot;/&amp;gt;
&amp;lt;rect x=&quot;284&quot; y=&quot;171&quot; width=&quot;4&quot; height=&quot;9&quot;/&amp;gt;
&amp;lt;rect x=&quot;288&quot; y=&quot;163&quot; width=&quot;4&quot; height=&quot;17&quot;/&amp;gt;
&amp;lt;rect x=&quot;292&quot; y=&quot;173&quot; width=&quot;4&quot; height=&quot;7&quot;/&amp;gt;
&amp;lt;rect x=&quot;296&quot; y=&quot;161&quot; width=&quot;4&quot; height=&quot;19&quot;/&amp;gt;
&amp;lt;rect x=&quot;300&quot; y=&quot;170&quot; width=&quot;4&quot; height=&quot;10&quot;/&amp;gt;
&amp;lt;rect x=&quot;304&quot; y=&quot;166&quot; width=&quot;4&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;308&quot; y=&quot;172&quot; width=&quot;4&quot; height=&quot;8&quot;/&amp;gt;
&amp;lt;rect x=&quot;312&quot; y=&quot;164&quot; width=&quot;4&quot; height=&quot;16&quot;/&amp;gt;
&amp;lt;rect x=&quot;320&quot; y=&quot;162&quot; width=&quot;4&quot; height=&quot;18&quot;/&amp;gt;
&amp;lt;rect x=&quot;324&quot; y=&quot;174&quot; width=&quot;4&quot; height=&quot;6&quot;/&amp;gt;
&amp;lt;rect x=&quot;328&quot; y=&quot;167&quot; width=&quot;4&quot; height=&quot;13&quot;/&amp;gt;
&amp;lt;rect x=&quot;332&quot; y=&quot;169&quot; width=&quot;4&quot; height=&quot;11&quot;/&amp;gt;
&amp;lt;rect x=&quot;336&quot; y=&quot;165&quot; width=&quot;4&quot; height=&quot;15&quot;/&amp;gt;
&amp;lt;rect x=&quot;340&quot; y=&quot;171&quot; width=&quot;4&quot; height=&quot;9&quot;/&amp;gt;
&amp;lt;rect x=&quot;344&quot; y=&quot;163&quot; width=&quot;4&quot; height=&quot;17&quot;/&amp;gt;
&amp;lt;rect x=&quot;348&quot; y=&quot;173&quot; width=&quot;4&quot; height=&quot;7&quot;/&amp;gt;
&amp;lt;rect x=&quot;352&quot; y=&quot;161&quot; width=&quot;4&quot; height=&quot;19&quot;/&amp;gt;
&amp;lt;rect x=&quot;356&quot; y=&quot;170&quot; width=&quot;4&quot; height=&quot;10&quot;/&amp;gt;
&amp;lt;rect x=&quot;360&quot; y=&quot;166&quot; width=&quot;4&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;364&quot; y=&quot;172&quot; width=&quot;4&quot; height=&quot;8&quot;/&amp;gt;
&amp;lt;rect x=&quot;368&quot; y=&quot;164&quot; width=&quot;4&quot; height=&quot;16&quot;/&amp;gt;
&amp;lt;rect x=&quot;372&quot; y=&quot;168&quot; width=&quot;4&quot; height=&quot;12&quot;/&amp;gt;
&amp;lt;rect x=&quot;376&quot; y=&quot;162&quot; width=&quot;4&quot; height=&quot;18&quot;/&amp;gt;
&amp;lt;rect x=&quot;380&quot; y=&quot;174&quot; width=&quot;4&quot; height=&quot;6&quot;/&amp;gt;
&amp;lt;rect x=&quot;384&quot; y=&quot;167&quot; width=&quot;4&quot; height=&quot;13&quot;/&amp;gt;
&amp;lt;rect x=&quot;388&quot; y=&quot;169&quot; width=&quot;4&quot; height=&quot;11&quot;/&amp;gt;
&amp;lt;rect x=&quot;392&quot; y=&quot;165&quot; width=&quot;4&quot; height=&quot;15&quot;/&amp;gt;
&amp;lt;rect x=&quot;396&quot; y=&quot;171&quot; width=&quot;4&quot; height=&quot;9&quot;/&amp;gt;
&amp;lt;rect x=&quot;404&quot; y=&quot;173&quot; width=&quot;4&quot; height=&quot;7&quot;/&amp;gt;
&amp;lt;rect x=&quot;408&quot; y=&quot;161&quot; width=&quot;4&quot; height=&quot;19&quot;/&amp;gt;
&amp;lt;rect x=&quot;412&quot; y=&quot;170&quot; width=&quot;4&quot; height=&quot;10&quot;/&amp;gt;
&amp;lt;rect x=&quot;416&quot; y=&quot;166&quot; width=&quot;4&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;420&quot; y=&quot;172&quot; width=&quot;4&quot; height=&quot;8&quot;/&amp;gt;
&amp;lt;rect x=&quot;424&quot; y=&quot;164&quot; width=&quot;4&quot; height=&quot;16&quot;/&amp;gt;
&amp;lt;rect x=&quot;428&quot; y=&quot;168&quot; width=&quot;4&quot; height=&quot;12&quot;/&amp;gt;
&amp;lt;rect x=&quot;432&quot; y=&quot;162&quot; width=&quot;4&quot; height=&quot;18&quot;/&amp;gt;
&amp;lt;rect x=&quot;436&quot; y=&quot;174&quot; width=&quot;4&quot; height=&quot;6&quot;/&amp;gt;
&amp;lt;rect x=&quot;440&quot; y=&quot;167&quot; width=&quot;4&quot; height=&quot;13&quot;/&amp;gt;
&amp;lt;rect x=&quot;444&quot; y=&quot;169&quot; width=&quot;4&quot; height=&quot;11&quot;/&amp;gt;
&amp;lt;rect x=&quot;448&quot; y=&quot;165&quot; width=&quot;4&quot; height=&quot;15&quot;/&amp;gt;
&amp;lt;rect x=&quot;452&quot; y=&quot;171&quot; width=&quot;4&quot; height=&quot;9&quot;/&amp;gt;
&amp;lt;rect x=&quot;456&quot; y=&quot;163&quot; width=&quot;4&quot; height=&quot;17&quot;/&amp;gt;
&amp;lt;rect x=&quot;460&quot; y=&quot;173&quot; width=&quot;4&quot; height=&quot;7&quot;/&amp;gt;
&amp;lt;rect x=&quot;464&quot; y=&quot;161&quot; width=&quot;4&quot; height=&quot;19&quot;/&amp;gt;
&amp;lt;rect x=&quot;468&quot; y=&quot;170&quot; width=&quot;4&quot; height=&quot;10&quot;/&amp;gt;
&amp;lt;rect x=&quot;472&quot; y=&quot;166&quot; width=&quot;4&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;476&quot; y=&quot;172&quot; width=&quot;4&quot; height=&quot;8&quot;/&amp;gt;
&amp;lt;rect x=&quot;480&quot; y=&quot;164&quot; width=&quot;4&quot; height=&quot;16&quot;/&amp;gt;
&amp;lt;rect x=&quot;488&quot; y=&quot;162&quot; width=&quot;4&quot; height=&quot;18&quot;/&amp;gt;
&amp;lt;rect x=&quot;492&quot; y=&quot;174&quot; width=&quot;4&quot; height=&quot;6&quot;/&amp;gt;
&amp;lt;rect x=&quot;496&quot; y=&quot;167&quot; width=&quot;4&quot; height=&quot;13&quot;/&amp;gt;
&amp;lt;rect x=&quot;500&quot; y=&quot;169&quot; width=&quot;4&quot; height=&quot;11&quot;/&amp;gt;
&amp;lt;rect x=&quot;504&quot; y=&quot;165&quot; width=&quot;4&quot; height=&quot;15&quot;/&amp;gt;
&amp;lt;rect x=&quot;508&quot; y=&quot;171&quot; width=&quot;4&quot; height=&quot;9&quot;/&amp;gt;
&amp;lt;rect x=&quot;512&quot; y=&quot;163&quot; width=&quot;4&quot; height=&quot;17&quot;/&amp;gt;
&amp;lt;rect x=&quot;516&quot; y=&quot;173&quot; width=&quot;4&quot; height=&quot;7&quot;/&amp;gt;
&amp;lt;rect x=&quot;520&quot; y=&quot;161&quot; width=&quot;4&quot; height=&quot;19&quot;/&amp;gt;
&amp;lt;rect x=&quot;524&quot; y=&quot;170&quot; width=&quot;4&quot; height=&quot;10&quot;/&amp;gt;
&amp;lt;rect x=&quot;528&quot; y=&quot;166&quot; width=&quot;4&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g fill=&quot;#B8651A&quot;&amp;gt;
&amp;lt;rect x=&quot;68&quot; y=&quot;125&quot; width=&quot;4&quot; height=&quot;55&quot;/&amp;gt;
&amp;lt;rect x=&quot;128&quot; y=&quot;130&quot; width=&quot;4&quot; height=&quot;50&quot;/&amp;gt;
&amp;lt;rect x=&quot;224&quot; y=&quot;124&quot; width=&quot;4&quot; height=&quot;56&quot;/&amp;gt;
&amp;lt;rect x=&quot;316&quot; y=&quot;132&quot; width=&quot;4&quot; height=&quot;48&quot;/&amp;gt;
&amp;lt;rect x=&quot;400&quot; y=&quot;128&quot; width=&quot;4&quot; height=&quot;52&quot;/&amp;gt;
&amp;lt;rect x=&quot;484&quot; y=&quot;134&quot; width=&quot;4&quot; height=&quot;46&quot;/&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;20&quot; y1=&quot;180&quot; x2=&quot;540&quot; y2=&quot;180&quot; stroke=&quot;#8F8578&quot; stroke-width=&quot;0.8&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;22&quot; y=&quot;194&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;e1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;540&quot; y=&quot;194&quot; text-anchor=&quot;end&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;e128&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;555&quot; y=&quot;135&quot; width=&quot;20&quot; height=&quot;45&quot; fill=&quot;#D1FAE5&quot; stroke=&quot;#15803D&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;text x=&quot;565&quot; y=&quot;158&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; font-weight=&quot;600&quot; fill=&quot;#15803D&quot;&amp;gt;+1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;565&quot; y=&quot;194&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#15803D&quot;&amp;gt;shared&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;280&quot; y=&quot;116&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; font-weight=&quot;600&quot; fill=&quot;#B8651A&quot;&amp;gt;top-6 routed (highest scores)&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;565&quot; y=&quot;123&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#15803D&quot;&amp;gt;always on&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g stroke=&quot;#B8651A&quot; stroke-width=&quot;0.8&quot; opacity=&quot;0.7&quot;&amp;gt;
&amp;lt;line x1=&quot;70&quot; y1=&quot;180&quot; x2=&quot;70&quot; y2=&quot;200&quot;/&amp;gt;
&amp;lt;line x1=&quot;130&quot; y1=&quot;180&quot; x2=&quot;130&quot; y2=&quot;200&quot;/&amp;gt;
&amp;lt;line x1=&quot;226&quot; y1=&quot;180&quot; x2=&quot;226&quot; y2=&quot;200&quot;/&amp;gt;
&amp;lt;line x1=&quot;318&quot; y1=&quot;180&quot; x2=&quot;318&quot; y2=&quot;200&quot;/&amp;gt;
&amp;lt;line x1=&quot;402&quot; y1=&quot;180&quot; x2=&quot;402&quot; y2=&quot;200&quot;/&amp;gt;
&amp;lt;line x1=&quot;486&quot; y1=&quot;180&quot; x2=&quot;486&quot; y2=&quot;200&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;line x1=&quot;565&quot; y1=&quot;180&quot; x2=&quot;565&quot; y2=&quot;200&quot; stroke=&quot;#15803D&quot; stroke-width=&quot;0.8&quot; opacity=&quot;0.7&quot;/&amp;gt;
&amp;lt;line x1=&quot;60&quot; y1=&quot;200&quot; x2=&quot;495&quot; y2=&quot;200&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;1.2&quot; opacity=&quot;0.7&quot;/&amp;gt;
&amp;lt;line x1=&quot;555&quot; y1=&quot;200&quot; x2=&quot;575&quot; y2=&quot;200&quot; stroke=&quot;#15803D&quot; stroke-width=&quot;1.2&quot; opacity=&quot;0.7&quot;/&amp;gt;
&amp;lt;line x1=&quot;278&quot; y1=&quot;201&quot; x2=&quot;332&quot; y2=&quot;223&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;0.9&quot; opacity=&quot;0.8&quot; marker-end=&quot;url(#a12r)&quot;/&amp;gt;
&amp;lt;line x1=&quot;565&quot; y1=&quot;201&quot; x2=&quot;408&quot; y2=&quot;223&quot; stroke=&quot;#15803D&quot; stroke-width=&quot;0.9&quot; opacity=&quot;0.8&quot; marker-end=&quot;url(#a12g)&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;240&quot; y=&quot;225&quot; width=&quot;200&quot; height=&quot;40&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;245&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#B8651A&quot;&amp;gt;Σ weighted sum&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;258&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;9&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;6 routed × score + 1 shared&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;340&quot; y1=&quot;267&quot; x2=&quot;340&quot; y2=&quot;282&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a12)&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;290&quot; y=&quot;284&quot; width=&quot;100&quot; height=&quot;22&quot; fill=&quot;#1A1A1A&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;300&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#F4EEE3&quot;&amp;gt;layer output&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 12.&amp;lt;/strong&amp;gt; What one MoE layer does for one token. The router runs a 128-way softmax and picks the six experts with the highest scores; the rest contribute nothing for this token. A separate &quot;shared&quot; expert runs for every token regardless of routing. The layer&apos;s output is a weighted sum of those six routed experts&apos; outputs (weighted by router score) plus the shared expert. Aggregated over a batch, almost every expert sees some token, but each individual token only touches the seven the router and the shared slot select.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;The arithmetic, in rough terms: 128 routed experts at some parameter count each, plus a shared expert. For each token, the router computes scores for all 128, picks the top 6, and the layer&apos;s output is a weighted combination of those 6 experts&apos; outputs (weighted by router scores) plus the shared expert&apos;s contribution. The 122 unselected experts contribute nothing to this token&apos;s output. Their expert MLPs cost substantial FLOPs only when &lt;em&gt;some&lt;/em&gt; token in the batch routes to them; the router still scores all 128 every step, but that&apos;s a cheap projection compared to the expert matmuls.&lt;/p&gt;
&lt;p&gt;Aggregated over a batch of N tokens, the shared expert sees 100% of the tokens (it isn&apos;t routed) and each routed expert sees on average about 6N/128 ≈ 4.7% of the tokens under balanced routing. So 30B total parameters, only ~3.5B active per token, but during a forward pass on a batch of, say, 1024 tokens, the shared expert and most of the routed experts touch &lt;em&gt;some&lt;/em&gt; token.&lt;/p&gt;
&lt;p&gt;Three things follow from this. Capacity scales with parameter count; you can keep adding experts. Compute scales with the active count, not the total. Memory bandwidth dominates inference, because all 30B parameters live in GPU memory and have to be addressable even if any one token only uses 3.5B of them.&lt;/p&gt;
&lt;p&gt;The third point is what NVFP4 attacks.&lt;/p&gt;
&lt;h3&gt;17. NVFP4: 4-bit weights, BF16 accuracy&lt;/h3&gt;
&lt;p&gt;The cheapest way to spend less memory bandwidth is to use fewer bits per weight. NVIDIA&apos;s Blackwell-era format for this is &lt;a href=&quot;https://developer.nvidia.com/blog/introducing-nvfp4-for-efficient-and-accurate-low-precision-inference/&quot;&gt;NVFP4&lt;/a&gt;, a 4-bit floating-point representation with a two-level scaling scheme. The format itself:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;NVFP4 employs a dual-tier approach: 1 shared FP8 scale per 16 value block (E4M3) and 1 FP32 per tensor second-level scaling factor.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Decoding the layout: each weight is stored as a 4-bit floating-point value with one sign bit, two exponent bits, and one mantissa bit (E2M1). Sixteen of those weights share an FP8 scale factor in E4M3 (the same format Hopper and Blackwell already had hardware support for). And the entire tensor shares a single FP32 global scale on top of that. The two-level structure lets the format keep both wide tensor-level dynamic range (the FP32 scale) and per-block precision tracking (the FP8 micro-scale), at a cost of about 5 effective bits per weight rather than a flat 4.&lt;/p&gt;
&lt;p&gt;Memory savings versus the alternatives, on the Nemotron 3 Nano Omni reasoning checkpoint specifically:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;The NVFP4 variant uses a mixed-precision recipe inspired by Nemotron 3 Super: routed MoE experts are quantized to NVFP4 (FP4 E2M1 values with per-block FP8 E4M3 scales over groups of 16 elements and an additional per-tensor FP32 global scale), while the Mamba in_proj / out_proj, shared experts, and attention o_proj are quantized to FP8, yielding 4.98 effective bits per weight (20.9 GB). In both variants the vision and audio encoders and their MLP projectors are kept in BF16.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The numbers, plain: BF16 weights for the full model are 61.5 GB. FP8 is 32.8 GB. NVFP4 (with the mixed-precision recipe) is 20.9 GB. The reasoning-checkpoint mean across nine multimodal benchmarks: BF16 65.80, NVFP4 65.43. A 0.38-point drop on the mean for a 3× memory reduction.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 340&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; style=&quot;height: auto&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;340&quot; y=&quot;20&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;NVFP4 STORAGE: ONE 16-WEIGHT BLOCK&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;120&quot; y=&quot;32&quot; width=&quot;440&quot; height=&quot;22&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;47&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#B8651A&quot;&amp;gt;FP32 global scale, 1 per tensor (32 bits)&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;120&quot; y=&quot;62&quot; width=&quot;440&quot; height=&quot;22&quot; fill=&quot;#D1FAE5&quot; stroke=&quot;#15803D&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;77&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-weight=&quot;600&quot; fill=&quot;#15803D&quot;&amp;gt;FP8 E4M3 micro-scale, 1 per 16-weight block (8 bits)&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;0.8&quot;&amp;gt;
&amp;lt;rect x=&quot;120&quot; y=&quot;92&quot; width=&quot;26&quot; height=&quot;32&quot;/&amp;gt;
&amp;lt;rect x=&quot;148&quot; y=&quot;92&quot; width=&quot;26&quot; height=&quot;32&quot;/&amp;gt;
&amp;lt;rect x=&quot;176&quot; y=&quot;92&quot; width=&quot;26&quot; height=&quot;32&quot;/&amp;gt;
&amp;lt;rect x=&quot;204&quot; y=&quot;92&quot; width=&quot;26&quot; height=&quot;32&quot;/&amp;gt;
&amp;lt;rect x=&quot;232&quot; y=&quot;92&quot; width=&quot;26&quot; height=&quot;32&quot;/&amp;gt;
&amp;lt;rect x=&quot;260&quot; y=&quot;92&quot; width=&quot;26&quot; height=&quot;32&quot;/&amp;gt;
&amp;lt;rect x=&quot;288&quot; y=&quot;92&quot; width=&quot;26&quot; height=&quot;32&quot;/&amp;gt;
&amp;lt;rect x=&quot;316&quot; y=&quot;92&quot; width=&quot;26&quot; height=&quot;32&quot;/&amp;gt;
&amp;lt;rect x=&quot;344&quot; y=&quot;92&quot; width=&quot;26&quot; height=&quot;32&quot;/&amp;gt;
&amp;lt;rect x=&quot;372&quot; y=&quot;92&quot; width=&quot;26&quot; height=&quot;32&quot;/&amp;gt;
&amp;lt;rect x=&quot;400&quot; y=&quot;92&quot; width=&quot;26&quot; height=&quot;32&quot;/&amp;gt;
&amp;lt;rect x=&quot;428&quot; y=&quot;92&quot; width=&quot;26&quot; height=&quot;32&quot;/&amp;gt;
&amp;lt;rect x=&quot;456&quot; y=&quot;92&quot; width=&quot;26&quot; height=&quot;32&quot;/&amp;gt;
&amp;lt;rect x=&quot;484&quot; y=&quot;92&quot; width=&quot;26&quot; height=&quot;32&quot;/&amp;gt;
&amp;lt;rect x=&quot;512&quot; y=&quot;92&quot; width=&quot;26&quot; height=&quot;32&quot;/&amp;gt;
&amp;lt;rect x=&quot;540&quot; y=&quot;92&quot; width=&quot;26&quot; height=&quot;32&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#2563EB&quot; text-anchor=&quot;middle&quot;&amp;gt;
&amp;lt;text x=&quot;133&quot; y=&quot;111&quot;&amp;gt;E2M1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;161&quot; y=&quot;111&quot;&amp;gt;E2M1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;189&quot; y=&quot;111&quot;&amp;gt;E2M1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;217&quot; y=&quot;111&quot;&amp;gt;E2M1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;245&quot; y=&quot;111&quot;&amp;gt;E2M1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;273&quot; y=&quot;111&quot;&amp;gt;E2M1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;301&quot; y=&quot;111&quot;&amp;gt;E2M1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;329&quot; y=&quot;111&quot;&amp;gt;E2M1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;357&quot; y=&quot;111&quot;&amp;gt;E2M1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;385&quot; y=&quot;111&quot;&amp;gt;E2M1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;413&quot; y=&quot;111&quot;&amp;gt;E2M1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;441&quot; y=&quot;111&quot;&amp;gt;E2M1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;469&quot; y=&quot;111&quot;&amp;gt;E2M1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;497&quot; y=&quot;111&quot;&amp;gt;E2M1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;525&quot; y=&quot;111&quot;&amp;gt;E2M1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;553&quot; y=&quot;111&quot;&amp;gt;E2M1&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;120&quot; y=&quot;138&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;w₁&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;566&quot; y=&quot;138&quot; text-anchor=&quot;end&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;w₁₆&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;line x1=&quot;146&quot; y1=&quot;148&quot; x2=&quot;146&quot; y2=&quot;160&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;0.8&quot;/&amp;gt;
&amp;lt;text x=&quot;146&quot; y=&quot;172&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#2563EB&quot;&amp;gt;each = 4 bits (1 sign + 2 exp + 1 mantissa)&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;340&quot; y=&quot;195&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#1A1A1A&quot;&amp;gt;total: &amp;lt;tspan font-family=&quot;JetBrains Mono&quot;&amp;gt;16 × 4 + 8 + 32/N = ~5 effective bits per weight&amp;lt;/tspan&amp;gt;&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;120&quot; y=&quot;220&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;30B-MODEL WEIGHTS IN GPU MEMORY&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;120&quot; y=&quot;232&quot; width=&quot;400&quot; height=&quot;18&quot; fill=&quot;#EDE5D4&quot; stroke=&quot;#8F8578&quot; stroke-width=&quot;0.5&quot;/&amp;gt;
&amp;lt;rect x=&quot;120&quot; y=&quot;232&quot; width=&quot;400&quot; height=&quot;18&quot; fill=&quot;#1A1A1A&quot; opacity=&quot;0.6&quot;/&amp;gt;
&amp;lt;text x=&quot;525&quot; y=&quot;245&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#1A1A1A&quot;&amp;gt;BF16: 61.5 GB&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;120&quot; y=&quot;256&quot; width=&quot;400&quot; height=&quot;18&quot; fill=&quot;#EDE5D4&quot; stroke=&quot;#8F8578&quot; stroke-width=&quot;0.5&quot;/&amp;gt;
&amp;lt;rect x=&quot;120&quot; y=&quot;256&quot; width=&quot;213&quot; height=&quot;18&quot; fill=&quot;#B8651A&quot; opacity=&quot;0.65&quot;/&amp;gt;
&amp;lt;text x=&quot;525&quot; y=&quot;269&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#B8651A&quot;&amp;gt;FP8: 32.8 GB&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;120&quot; y=&quot;280&quot; width=&quot;400&quot; height=&quot;18&quot; fill=&quot;#EDE5D4&quot; stroke=&quot;#8F8578&quot; stroke-width=&quot;0.5&quot;/&amp;gt;
&amp;lt;rect x=&quot;120&quot; y=&quot;280&quot; width=&quot;136&quot; height=&quot;18&quot; fill=&quot;#15803D&quot; opacity=&quot;0.65&quot;/&amp;gt;
&amp;lt;text x=&quot;525&quot; y=&quot;293&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#15803D&quot;&amp;gt;NVFP4 (mixed): 20.9 GB&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;340&quot; y=&quot;320&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;3× memory reduction over BF16 with a ~0.4-point mean benchmark drop&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 13.&amp;lt;/strong&amp;gt; The NVFP4 storage layout. Each weight in a 16-weight block is stored as a 4-bit E2M1 floating-point value (1 sign bit, 2 exponent bits, 1 mantissa bit). The block shares a single FP8 E4M3 micro-scale, and the entire tensor shares one FP32 global scale on top. Effective storage is around 5 bits per weight. Bottom: the same 30B model occupies 61.5 GB at BF16, 32.8 GB at FP8, and 20.9 GB under the NVFP4 mixed-precision recipe.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;The mixed-precision recipe matters. Quantizing the routed experts to 4 bits is the bulk of the savings (most parameters in a 30B-A3B MoE live in those experts). Mamba projection layers, shared experts, and the attention output projection stay at FP8 because those layers are more sensitive. Vision and audio encoders stay at BF16 because they&apos;re small relative to the LLM and the accuracy cost isn&apos;t worth it.&lt;/p&gt;
&lt;p&gt;The reason this works is that NVFP4 reuses E4M3 as the per-block scale factor. Blackwell already has hardware for E4M3, so the micro-scale comes nearly for free. The format is a careful reuse of formats Blackwell already accelerates, not an exotic ad-hoc design.&lt;/p&gt;
&lt;h3&gt;18. What&apos;s still missing&lt;/h3&gt;
&lt;p&gt;The post just walked through how to wire perception across four modalities into a single decoder. The reasoning loop on top of that perception is a different and unfinished problem.&lt;/p&gt;
&lt;p&gt;What the model has at this point: a unified backbone that reads text, sees images, hears audio, and watches video, all in one forward pass; a hybrid Mamba+attention+MoE architecture that scales to 262K-token contexts; a quantization recipe that fits the whole thing in around 21 GB of weights. The model card lists tool-calling support and reports OSWorld-style computer-use scores, so the post is not making the claim that no agentic numbers exist; what it is claiming is that the broader question of long-horizon planning, multi-step decomposition, and benchmark performance on suites like BFCL or GAIA is not yet what the report or model cards center on. The launch announcement frames the model as a &quot;multimodal perception-and-context sub-agent within larger agentic systems,&quot; not as a planner.&lt;/p&gt;
&lt;p&gt;That framing is honest and worth taking seriously. Perception is not reasoning. A model that perceives well is a foundation for an agent loop; it is not the loop itself. The next problem in the stack is how the LLM in the middle generates &lt;em&gt;plans&lt;/em&gt;, calls &lt;em&gt;tools&lt;/em&gt;, and executes &lt;em&gt;multi-step trajectories&lt;/em&gt; over the perceived state. None of that has the same shape as the perception story this post traced.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 365&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; style=&quot;height: auto&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text x=&quot;340&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;EFFICIENCY EVIDENCE LEDGER (NEMOTRON-FAMILY MEASUREMENTS)&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;10&quot; y=&quot;35&quot; width=&quot;660&quot; height=&quot;25&quot; fill=&quot;#1A1A1A&quot;/&amp;gt;
&amp;lt;g font-family=&quot;serif&quot; font-size=&quot;11&quot; font-weight=&quot;700&quot; fill=&quot;#F4EEE3&quot;&amp;gt;
&amp;lt;text x=&quot;20&quot; y=&quot;52&quot;&amp;gt;Claim&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;52&quot;&amp;gt;Mechanism&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;370&quot; y=&quot;52&quot;&amp;gt;Number (vs baseline)&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;570&quot; y=&quot;52&quot;&amp;gt;Limitation&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g stroke=&quot;#1A1A1A&quot; stroke-width=&quot;0.4&quot; opacity=&quot;0.3&quot;&amp;gt;
&amp;lt;line x1=&quot;160&quot; y1=&quot;35&quot; x2=&quot;160&quot; y2=&quot;358&quot;/&amp;gt;
&amp;lt;line x1=&quot;360&quot; y1=&quot;35&quot; x2=&quot;360&quot; y2=&quot;358&quot;/&amp;gt;
&amp;lt;line x1=&quot;560&quot; y1=&quot;35&quot; x2=&quot;560&quot; y2=&quot;358&quot;/&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;10&quot; y=&quot;63&quot; width=&quot;660&quot; height=&quot;70&quot; fill=&quot;#F9F5EB&quot; stroke=&quot;#C9BEAA&quot; stroke-width=&quot;0.5&quot;/&amp;gt;
&amp;lt;rect x=&quot;10&quot; y=&quot;63&quot; width=&quot;4&quot; height=&quot;70&quot; fill=&quot;#2563EB&quot;/&amp;gt;
&amp;lt;g font-family=&quot;serif&quot; font-size=&quot;11&quot; fill=&quot;#1A1A1A&quot;&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;80&quot; font-weight=&quot;600&quot; fill=&quot;#2563EB&quot;&amp;gt;Long-context&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;94&quot; font-weight=&quot;600&quot; fill=&quot;#2563EB&quot;&amp;gt;inference&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;114&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot; font-size=&quot;10&quot;&amp;gt;throughput&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;80&quot;&amp;gt;Mamba-2 selective&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;94&quot;&amp;gt;SSM, O(N) cost in&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;108&quot;&amp;gt;sequence length, plus&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;122&quot;&amp;gt;6 attention for retrieval&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;370&quot; y=&quot;80&quot;&amp;gt;2.9× tokens/sec at 64K&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;370&quot; y=&quot;94&quot;&amp;gt;input, 1024 output, on H100&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;370&quot; y=&quot;114&quot; font-style=&quot;italic&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot;&amp;gt;(vs Qwen-2.5-72B, Llama-3.1-70B)&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;570&quot; y=&quot;80&quot; font-size=&quot;10&quot;&amp;gt;Measured on&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;570&quot; y=&quot;94&quot; font-size=&quot;10&quot;&amp;gt;Nemotron-H-47B,&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;570&quot; y=&quot;108&quot; font-size=&quot;10&quot;&amp;gt;not on Nemotron 3&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;570&quot; y=&quot;122&quot; font-size=&quot;10&quot;&amp;gt;Nano Omni&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;10&quot; y=&quot;138&quot; width=&quot;660&quot; height=&quot;70&quot; fill=&quot;#F9F5EB&quot; stroke=&quot;#C9BEAA&quot; stroke-width=&quot;0.5&quot;/&amp;gt;
&amp;lt;rect x=&quot;10&quot; y=&quot;138&quot; width=&quot;4&quot; height=&quot;70&quot; fill=&quot;#15803D&quot;/&amp;gt;
&amp;lt;g font-family=&quot;serif&quot; font-size=&quot;11&quot; fill=&quot;#1A1A1A&quot;&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;155&quot; font-weight=&quot;600&quot; fill=&quot;#15803D&quot;&amp;gt;Active&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;169&quot; font-weight=&quot;600&quot; fill=&quot;#15803D&quot;&amp;gt;parameters&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;189&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot; font-size=&quot;10&quot;&amp;gt;per token&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;155&quot;&amp;gt;MoE sparse routing:&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;169&quot;&amp;gt;top-6 of 128 experts&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;183&quot;&amp;gt;+ 1 always-on shared,&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;197&quot;&amp;gt;router decides per token&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;370&quot; y=&quot;155&quot;&amp;gt;3.5B active /&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;370&quot; y=&quot;169&quot;&amp;gt;30B total params&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;370&quot; y=&quot;189&quot; font-style=&quot;italic&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot;&amp;gt;(per Nemotron 3 Nano Omni model card)&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;570&quot; y=&quot;155&quot; font-size=&quot;10&quot;&amp;gt;Wall-clock win&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;570&quot; y=&quot;169&quot; font-size=&quot;10&quot;&amp;gt;depends on serving&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;570&quot; y=&quot;183&quot; font-size=&quot;10&quot;&amp;gt;stack and batch&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;570&quot; y=&quot;197&quot; font-size=&quot;10&quot;&amp;gt;routing balance&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;10&quot; y=&quot;213&quot; width=&quot;660&quot; height=&quot;70&quot; fill=&quot;#F9F5EB&quot; stroke=&quot;#C9BEAA&quot; stroke-width=&quot;0.5&quot;/&amp;gt;
&amp;lt;rect x=&quot;10&quot; y=&quot;213&quot; width=&quot;4&quot; height=&quot;70&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;g font-family=&quot;serif&quot; font-size=&quot;11&quot; fill=&quot;#1A1A1A&quot;&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;230&quot; font-weight=&quot;600&quot; fill=&quot;#B8651A&quot;&amp;gt;Memory&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;244&quot; font-weight=&quot;600&quot; fill=&quot;#B8651A&quot;&amp;gt;footprint&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;230&quot;&amp;gt;NVFP4 mixed precision:&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;244&quot;&amp;gt;routed experts FP4,&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;258&quot;&amp;gt;attention and Mamba projs FP8,&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;272&quot;&amp;gt;vision/audio encoders BF16&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;370&quot; y=&quot;230&quot;&amp;gt;61.5 GB (BF16) →&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;370&quot; y=&quot;244&quot;&amp;gt;20.9 GB (NVFP4) on&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;370&quot; y=&quot;258&quot;&amp;gt;Nemotron 3 Nano Omni&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;370&quot; y=&quot;272&quot; font-style=&quot;italic&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot;&amp;gt;(3× reduction)&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;570&quot; y=&quot;230&quot; font-size=&quot;10&quot;&amp;gt;−0.38-point mean&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;570&quot; y=&quot;244&quot; font-size=&quot;10&quot;&amp;gt;drop across 9&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;570&quot; y=&quot;258&quot; font-size=&quot;10&quot;&amp;gt;multimodal&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;570&quot; y=&quot;272&quot; font-size=&quot;10&quot;&amp;gt;benchmarks&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;10&quot; y=&quot;288&quot; width=&quot;660&quot; height=&quot;70&quot; fill=&quot;#F9F5EB&quot; stroke=&quot;#C9BEAA&quot; stroke-width=&quot;0.5&quot;/&amp;gt;
&amp;lt;rect x=&quot;10&quot; y=&quot;288&quot; width=&quot;4&quot; height=&quot;70&quot; fill=&quot;#9333EA&quot;/&amp;gt;
&amp;lt;g font-family=&quot;serif&quot; font-size=&quot;11&quot; fill=&quot;#1A1A1A&quot;&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;305&quot; font-weight=&quot;600&quot; fill=&quot;#9333EA&quot;&amp;gt;Prosody&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;22&quot; y=&quot;319&quot; font-weight=&quot;600&quot; fill=&quot;#9333EA&quot;&amp;gt;preservation&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;305&quot;&amp;gt;Speech-out reads LLM&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;319&quot;&amp;gt;hidden states directly&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;333&quot;&amp;gt;(Qwen2.5-Omni Talker:&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;170&quot; y=&quot;347&quot;&amp;gt;no transcript bottleneck)&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;370&quot; y=&quot;305&quot;&amp;gt;Tsiamas et al. 2024:&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;370&quot; y=&quot;319&quot;&amp;gt;cascades transmit prosody&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;370&quot; y=&quot;333&quot;&amp;gt;to a &quot;lesser extent&quot;&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;570&quot; y=&quot;305&quot; font-size=&quot;10&quot;&amp;gt;Qualitative;&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;570&quot; y=&quot;319&quot; font-size=&quot;10&quot;&amp;gt;no Nemotron 3&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;570&quot; y=&quot;333&quot; font-size=&quot;10&quot;&amp;gt;Nano Omni&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;570&quot; y=&quot;347&quot; font-size=&quot;10&quot;&amp;gt;benchmark&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 14.&amp;lt;/strong&amp;gt; What the post can claim and where each claim comes from. Two measured efficiency wins on Nemotron-family models against named baselines (long-context throughput from Nemotron-H, memory footprint from Nemotron 3 Nano Omni&apos;s NVFP4 recipe), one structural claim from the model card (3.5B active out of 30B total per token), and a fourth qualitative claim about prosody. The post does not promise a controlled benchmark of &quot;unified-decoder beats stitched cascades&quot; in the abstract; it reports these four rows and stops.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;Three architectural moves take you from a vision backbone to a four-modality perception model. A text-centric autoregressive decoder. Modality-specific encoders feeding it through MLP projectors. A stack of token-budget mechanisms (tubelet fusion, EVS, hybrid Mamba+attention+MoE, NVFP4) to keep inference affordable. The last one is what makes the first two shippable.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;None of them are agent reasoning. But you can&apos;t reason without seeing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://augusteo.com/blog/unified-vision-stack&quot;&gt;The Unified Vision Stack&lt;/a&gt;. the prequel post, Augusteo 2026.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2103.00020&quot;&gt;Learning Transferable Visual Models From Natural Language Supervision (CLIP)&lt;/a&gt;. Radford et al., OpenAI 2021.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2303.15343&quot;&gt;Sigmoid Loss for Language Image Pre-Training (SigLIP)&lt;/a&gt;. Zhai, Mustafa, Kolesnikov, Beyer (Google DeepMind) 2023.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2502.14786&quot;&gt;SigLIP 2: Multilingual Vision-Language Encoders with Improved Semantic Understanding, Localization, and Dense Features&lt;/a&gt;. Tschannen et al. (Google DeepMind) 2025.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2312.06709&quot;&gt;AM-RADIO: Agglomerative Vision Foundation Model&lt;/a&gt;. Ranzinger et al. (NVIDIA) 2023, the framework that became C-RADIO.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2412.07679&quot;&gt;RADIOv2.5: Improved Baselines for Agglomerative Vision Foundation Models&lt;/a&gt;. Heinrich et al. (NVIDIA) 2024.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2204.14198&quot;&gt;Flamingo: a Visual Language Model for Few-Shot Learning&lt;/a&gt;. Alayrac et al. (DeepMind) 2022.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2301.12597&quot;&gt;BLIP-2: Bootstrapping Language-Image Pre-training with Frozen Image Encoders and Large Language Models&lt;/a&gt;. Li et al. (Salesforce) 2023.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2304.08485&quot;&gt;Visual Instruction Tuning (LLaVA)&lt;/a&gt;. Liu, Li, Wu, Lee 2023.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2310.03744&quot;&gt;Improved Baselines with Visual Instruction Tuning (LLaVA-1.5)&lt;/a&gt;. Liu et al. 2023.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2502.13923&quot;&gt;Qwen2.5-VL Technical Report&lt;/a&gt;. Bai et al. (Alibaba Qwen Team) 2025.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2503.20215&quot;&gt;Qwen2.5-Omni Technical Report&lt;/a&gt;. Xu et al. (Alibaba Cloud Qwen Team) 2025.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2212.04356&quot;&gt;Whisper: large-scale weak-supervision speech recognition&lt;/a&gt;. Radford et al. (OpenAI) 2022.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2410.24019&quot;&gt;Speech is More Than Words&lt;/a&gt;. Tsiamas, Sperber, Finch, Garg 2024 (cascade vs end-to-end speech translation, prosody study).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2405.09818&quot;&gt;Chameleon: Mixed-Modal Early-Fusion Foundation Models&lt;/a&gt;. Chameleon Team (FAIR at Meta) 2024.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2312.11805&quot;&gt;Gemini: A Family of Highly Capable Multimodal Models&lt;/a&gt;. Gemini Team (Google) 2023.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://research.nvidia.com/labs/nemotron/files/NVIDIA-Nemotron-3-Omni-report.pdf&quot;&gt;NVIDIA Nemotron 3 Omni technical report&lt;/a&gt;. NVIDIA 2026.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://huggingface.co/blog/nvidia/nemotron-3-nano-omni-multimodal-intelligence&quot;&gt;Introducing NVIDIA Nemotron 3 Nano Omni: Multimodal Intelligence&lt;/a&gt;. NVIDIA 2026 (HF blog).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://huggingface.co/nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-BF16&quot;&gt;Nemotron-3-Nano-Omni-30B-A3B-Reasoning-BF16 model card&lt;/a&gt;. NVIDIA 2026.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://huggingface.co/nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-NVFP4&quot;&gt;Nemotron-3-Nano-Omni-30B-A3B-Reasoning-NVFP4 model card&lt;/a&gt;. NVIDIA 2026.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://huggingface.co/nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16&quot;&gt;NVIDIA-Nemotron-3-Nano-30B-A3B-BF16 base model card&lt;/a&gt;. NVIDIA 2026.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://huggingface.co/nvidia/parakeet-tdt-0.6b-v2&quot;&gt;parakeet-tdt-0.6b-v2 model card&lt;/a&gt;. NVIDIA 2025.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2312.00752&quot;&gt;Mamba: Linear-Time Sequence Modeling with Selective State Spaces&lt;/a&gt;. Gu and Dao 2023.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2405.21060&quot;&gt;Transformers are SSMs (Mamba-2)&lt;/a&gt;. Dao and Gu 2024.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2504.03624&quot;&gt;Nemotron-H: A Family of Accurate and Efficient Hybrid Mamba-Transformer Models&lt;/a&gt;. NVIDIA 2025.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://research.nvidia.com/labs/adlr/nemotronh/&quot;&gt;Nemotron-H research page&lt;/a&gt;. NVIDIA ADLR 2025.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2403.19887&quot;&gt;Jamba: A Hybrid Transformer-Mamba Language Model&lt;/a&gt;. AI21 Labs 2024.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2101.03961&quot;&gt;Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity&lt;/a&gt;. Fedus, Zoph, Shazeer 2021.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2401.04088&quot;&gt;Mixtral of Experts&lt;/a&gt;. Mistral AI 2024.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.nvidia.com/blog/introducing-nvfp4-for-efficient-and-accurate-low-precision-inference/&quot;&gt;Introducing NVFP4 for Efficient and Accurate Low-Precision Inference&lt;/a&gt;. NVIDIA Developer Blog 2025.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2209.05433&quot;&gt;FP8 Formats for Deep Learning&lt;/a&gt;. Micikevicius et al. (NVIDIA, Arm, Intel) 2022.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2103.15691&quot;&gt;ViViT: A Video Vision Transformer&lt;/a&gt;. Arnab et al. (Google) 2021.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2102.05095&quot;&gt;Is Space-Time Attention All You Need for Video Understanding? (TimeSformer)&lt;/a&gt;. Bertasius, Wang, Torresani (Facebook AI) 2021.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2203.12602&quot;&gt;VideoMAE: Masked Autoencoders are Data-Efficient Learners for Self-Supervised Video Pre-Training&lt;/a&gt;. Tong, Song, Wang, Wang 2022.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2303.16727&quot;&gt;VideoMAE V2: Scaling Video Masked Autoencoders with Dual Masking&lt;/a&gt;. Wang et al. 2023.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2510.14624&quot;&gt;Efficient Video Sampling (EVS): Token Reduction for Long Videos in Multimodal LLMs&lt;/a&gt;. Bagrov et al. (NVIDIA + Deci) 2025.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Sequel to &lt;a href=&quot;/blog/unified-vision-stack&quot;&gt;The Unified Vision Stack&lt;/a&gt;, written May 2026.&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>How PgBouncer Works</title><link>https://www.augusteo.com/blog/how-pgbouncer-works/</link><guid isPermaLink="true">https://www.augusteo.com/blog/how-pgbouncer-works/</guid><description>A from-the-ground-up explanation of PgBouncer: why Postgres needs a connection pooler, what each pooling mode actually does, what breaks under transaction pooling, and how to run the thing in production.</description><pubDate>Thu, 30 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;A from-the-ground-up explanation of PgBouncer: why Postgres needs a connection pooler, what each pooling mode actually does, what breaks under transaction pooling, and how to run the thing in production. About a 30-minute read. Versions reference PgBouncer 1.25.1, the current stable as of writing.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;1. The thing PgBouncer fixes&lt;/h3&gt;
&lt;p&gt;If you have used Postgres for any length of time, you have probably run into some version of this story. Your app has been fine for months. Then you push a release that adds a few more workers, or traffic spikes during a launch, or your background job system starts running more parallel jobs than it used to. Suddenly the database starts refusing connections. Latency goes up. Memory usage on the database host climbs to ninety-something percent. The error logs are full of &lt;code&gt;FATAL: sorry, too many clients already&lt;/code&gt;, and your &lt;code&gt;max_connections&lt;/code&gt; setting, which seemed generous when you set it, is now the thing standing between you and a working application.&lt;/p&gt;
&lt;p&gt;The textbook fix is &quot;add a connection pooler.&quot; The textbook recommendation is PgBouncer. And then, if you are like most people, you set &lt;code&gt;pool_mode = transaction&lt;/code&gt;, point your app at port 6432 instead of 5432, and walk away thinking the problem is solved.&lt;/p&gt;
&lt;p&gt;It usually is. But the thing you just deployed is doing something quite specific, and if you do not understand what, the next bug you hit will be confusing in a way that direct Postgres bugs are not. Why doesn&apos;t &lt;code&gt;SET search_path&lt;/code&gt; stick? Why did my long-running report time out at 5 seconds when I never set a 5-second timeout? Why does this Perl script work when I connect directly but fail through PgBouncer? PgBouncer presents itself as a transparent proxy, and in one mode it almost is, but in the mode you actually want to use, it isn&apos;t, and the gap between what looks transparent and what actually is transparent is where the bugs live.&lt;/p&gt;
&lt;p&gt;This piece is about that gap. We&apos;ll start with why Postgres connections cost something in the first place, work through the three pooling modes and what each one buys and breaks, then look at how to actually run PgBouncer in production: how to size the pool, how to authenticate without storing every password twice, how to monitor it, and the long list of small things that go subtly wrong.&lt;/p&gt;
&lt;h2&gt;Act 1 — Why connections are expensive&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;The Postgres process model and why it does not love thousands of clients.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;2. The fork that started it all&lt;/h3&gt;
&lt;p&gt;Postgres has been around since 1986. Its connection model dates from a time when the modern web did not exist and the typical workload was a handful of analysts running queries from terminals. The model is simple. There is one supervisor process called the &lt;em&gt;postmaster&lt;/em&gt;. When a client connects, the postmaster calls &lt;code&gt;fork()&lt;/code&gt; and produces a new child process called a &lt;em&gt;backend&lt;/em&gt;. That backend is yours, exclusively, for the lifetime of the connection. When you disconnect, it exits. Every query you run, every transaction, every &lt;code&gt;SET&lt;/code&gt;, every prepared statement, every temporary table, lives inside that one process.&lt;/p&gt;
&lt;p&gt;This design has real virtues. Each connection is isolated, so a memory bug in one query cannot corrupt another client&apos;s session. Cancellation is a SIGINT to a specific PID. The OS scheduler does the heavy lifting of fairness between connections. It is a very Unix design and it ages well in many ways.&lt;/p&gt;
&lt;p&gt;It does not age well in one specific way. A process is not free. Forking it is not free. Initializing the backend&apos;s catalog cache is not free. And keeping it around when it is doing nothing is not free either.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 700 320&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Postgres process tree showing one postmaster forking a backend per client&quot; width=&quot;100%&quot; height=&quot;auto&quot;&amp;gt;
&amp;lt;rect fill=&quot;#f4ede0&quot; x=&quot;0&quot; y=&quot;0&quot; width=&quot;700&quot; height=&quot;320&quot; rx=&quot;6&quot;/&amp;gt;
&amp;lt;rect x=&quot;270&quot; y=&quot;30&quot; width=&quot;160&quot; height=&quot;46&quot; rx=&quot;4&quot; fill=&quot;#fff&quot; stroke=&quot;#1a1815&quot; stroke-width=&quot;1.4&quot;/&amp;gt;
&amp;lt;text font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1a1815&quot; x=&quot;350&quot; y=&quot;50&quot; text-anchor=&quot;middle&quot;&amp;gt;postmaster&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;66&quot; text-anchor=&quot;middle&quot;&amp;gt;listens on 5432&amp;lt;/text&amp;gt;
&amp;lt;g stroke=&quot;#4a4742&quot; fill=&quot;none&quot; stroke-width=&quot;1&quot;&amp;gt;
&amp;lt;path d=&quot;M 350 76 L 110 130&quot; stroke-dasharray=&quot;3 3&quot;/&amp;gt;
&amp;lt;path d=&quot;M 350 76 L 220 130&quot; stroke-dasharray=&quot;3 3&quot;/&amp;gt;
&amp;lt;path d=&quot;M 350 76 L 350 130&quot; stroke-dasharray=&quot;3 3&quot;/&amp;gt;
&amp;lt;path d=&quot;M 350 76 L 480 130&quot; stroke-dasharray=&quot;3 3&quot;/&amp;gt;
&amp;lt;path d=&quot;M 350 76 L 600 130&quot; stroke-dasharray=&quot;3 3&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;130&quot; width=&quot;140&quot; height=&quot;58&quot; rx=&quot;4&quot; fill=&quot;#fff&quot; stroke=&quot;#6a7d63&quot; stroke-width=&quot;1.4&quot;/&amp;gt;
&amp;lt;text font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1a1815&quot; x=&quot;110&quot; y=&quot;150&quot; text-anchor=&quot;middle&quot;&amp;gt;backend&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;110&quot; y=&quot;167&quot; text-anchor=&quot;middle&quot;&amp;gt;pid 1812&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;110&quot; y=&quot;180&quot; text-anchor=&quot;middle&quot;&amp;gt;~5–15 MB&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;rect x=&quot;150&quot; y=&quot;130&quot; width=&quot;140&quot; height=&quot;58&quot; rx=&quot;4&quot; fill=&quot;#fff&quot; stroke=&quot;#6a7d63&quot; stroke-width=&quot;1.4&quot;/&amp;gt;
&amp;lt;text font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1a1815&quot; x=&quot;220&quot; y=&quot;150&quot; text-anchor=&quot;middle&quot;&amp;gt;backend&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;220&quot; y=&quot;167&quot; text-anchor=&quot;middle&quot;&amp;gt;pid 1813&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;220&quot; y=&quot;180&quot; text-anchor=&quot;middle&quot;&amp;gt;idle&amp;lt;/text&amp;gt;

&amp;lt;rect x=&quot;280&quot; y=&quot;130&quot; width=&quot;140&quot; height=&quot;58&quot; rx=&quot;4&quot; fill=&quot;#fff&quot; stroke=&quot;#6a7d63&quot; stroke-width=&quot;1.4&quot;/&amp;gt;
&amp;lt;text font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1a1815&quot; x=&quot;350&quot; y=&quot;150&quot; text-anchor=&quot;middle&quot;&amp;gt;backend&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;167&quot; text-anchor=&quot;middle&quot;&amp;gt;pid 1814&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;180&quot; text-anchor=&quot;middle&quot;&amp;gt;running query&amp;lt;/text&amp;gt;

&amp;lt;rect x=&quot;410&quot; y=&quot;130&quot; width=&quot;140&quot; height=&quot;58&quot; rx=&quot;4&quot; fill=&quot;#fff&quot; stroke=&quot;#6a7d63&quot; stroke-width=&quot;1.4&quot;/&amp;gt;
&amp;lt;text font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1a1815&quot; x=&quot;480&quot; y=&quot;150&quot; text-anchor=&quot;middle&quot;&amp;gt;backend&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;480&quot; y=&quot;167&quot; text-anchor=&quot;middle&quot;&amp;gt;pid 1815&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;480&quot; y=&quot;180&quot; text-anchor=&quot;middle&quot;&amp;gt;idle in tx&amp;lt;/text&amp;gt;

&amp;lt;rect x=&quot;540&quot; y=&quot;130&quot; width=&quot;140&quot; height=&quot;58&quot; rx=&quot;4&quot; fill=&quot;#fff&quot; stroke=&quot;#6a7d63&quot; stroke-width=&quot;1.4&quot;/&amp;gt;
&amp;lt;text font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1a1815&quot; x=&quot;610&quot; y=&quot;150&quot; text-anchor=&quot;middle&quot;&amp;gt;backend&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;610&quot; y=&quot;167&quot; text-anchor=&quot;middle&quot;&amp;gt;pid 1816&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;610&quot; y=&quot;180&quot; text-anchor=&quot;middle&quot;&amp;gt;idle&amp;lt;/text&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/g&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;110&quot; y=&quot;240&quot; text-anchor=&quot;middle&quot;&amp;gt;client A&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;220&quot; y=&quot;240&quot; text-anchor=&quot;middle&quot;&amp;gt;client B&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;240&quot; text-anchor=&quot;middle&quot;&amp;gt;client C&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;480&quot; y=&quot;240&quot; text-anchor=&quot;middle&quot;&amp;gt;client D&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;610&quot; y=&quot;240&quot; text-anchor=&quot;middle&quot;&amp;gt;client E&amp;lt;/text&amp;gt;
&amp;lt;g stroke=&quot;#4a4742&quot; fill=&quot;none&quot; stroke-width=&quot;1&quot;&amp;gt;
&amp;lt;path d=&quot;M 110 220 L 110 195&quot;/&amp;gt;
&amp;lt;path d=&quot;M 220 220 L 220 195&quot;/&amp;gt;
&amp;lt;path d=&quot;M 350 220 L 350 195&quot;/&amp;gt;
&amp;lt;path d=&quot;M 480 220 L 480 195&quot;/&amp;gt;
&amp;lt;path d=&quot;M 610 220 L 610 195&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;290&quot; text-anchor=&quot;middle&quot;&amp;gt;one process per client. always. even when the client is doing nothing.&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 1.&amp;lt;/strong&amp;gt; The Postgres connection model. Every client gets a dedicated OS process. The processes are isolated, which is great for stability, and unshared, which is bad for scaling.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;h3&gt;3. What an idle connection actually costs&lt;/h3&gt;
&lt;p&gt;Memory is the headline cost, and the number you usually hear is &quot;10 MB per connection.&quot; That number is roughly right but slightly misleading. The truth is that &lt;code&gt;ps&lt;/code&gt; and &lt;code&gt;top&lt;/code&gt; overcount, because Linux uses copy-on-write for forked processes and does not split out shared memory cleanly in those tools. When you measure more carefully — &lt;a href=&quot;https://blog.anarazel.de/2020/10/07/measuring-the-memory-overhead-of-a-postgres-connection/&quot;&gt;Andres Freund&apos;s writeup&lt;/a&gt; uses a mix of &lt;code&gt;/proc/[pid]/smaps_rollup&lt;/code&gt; (an approximation, not an exact figure) and direct PSS accounting — the incremental private memory of an idle Postgres backend is closer to 1.5 to 2 MB on a fresh connection, growing to 5 to 15 MB once the connection has done some work and accumulated catalog cache, prepared plans, and per-process state. The AWS database team&apos;s RDS-side measurements landed at 1.5 to 14.5 MB depending on workload, which lines up.&lt;/p&gt;
&lt;p&gt;So it is not &quot;10 MB per connection&quot; exactly. It is more like &quot;a few MB per connection, more if it does anything interesting, multiplied by however many connections you have, even if they are idle.&quot; On a host with 32 GB of RAM, where you also want most of that going to &lt;code&gt;shared_buffers&lt;/code&gt; and the OS page cache, you cannot afford 2,000 idle connections. You probably cannot afford 1,000.&lt;/p&gt;
&lt;p&gt;Memory is not even the whole story. Each backend process is also a thing the OS scheduler has to consider. Each backend has a slot in Postgres&apos; shared procarray, which is scanned during snapshot acquisition: a process that does nothing all day still gets walked over every time another transaction takes a snapshot. There&apos;s a known cliff somewhere in the low thousands of connections where Postgres spends measurably more CPU just managing the procarray than running queries. The exact number depends on hardware and version, but as a rule of thumb, past a few hundred connections per CPU core, you are buying overhead and not throughput.&lt;/p&gt;
&lt;p&gt;Then there is connection setup cost. Opening a fresh Postgres connection involves a TCP handshake, a TLS handshake (in any sane production setup), a fork on the server, a SCRAM exchange, sending startup parameters, loading the role&apos;s config, and setting the search path. On a healthy LAN this takes a few milliseconds. On a TLS connection across regions, it can be 30 to 100 ms. PHP-style &quot;connect on every request&quot; patterns make this a per-request tax. Hans-Jürgen Schönig benchmarked the difference between connection-per-query and pooled-connection at 98.7% throughput loss, going from a couple hundred thousand transactions per second down to 3,770. The fork is most of it.&lt;/p&gt;
&lt;p&gt;So a Postgres connection costs you four things: memory, scheduler overhead, snapshot-acquisition overhead, and the multi-millisecond setup tax that fires every time a new one is opened. Any one of these gets bad at scale. All four together are why you cannot solve &quot;scale to a thousand app workers&quot; by just raising &lt;code&gt;max_connections&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Raising &lt;code&gt;max_connections&lt;/code&gt; is not scaling. It&apos;s just postponing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;4. What we actually want&lt;/h3&gt;
&lt;p&gt;Here&apos;s the observation that motivates everything that follows. Most application connections are doing nothing most of the time. A web request typically spends 1 ms talking to the database and 50 ms doing other work: rendering, calling APIs, formatting responses, waiting for the user. A worker process holds a connection for its lifetime even though it spends 95% of that lifetime idle.&lt;/p&gt;
&lt;p&gt;If you could somehow let many app-side connections share a smaller pool of real Postgres connections, multiplexed so that whichever client actually needs the database right now gets a real connection, and everyone else gets a cheap proxy connection that costs essentially nothing while they&apos;re idle, you&apos;d have your scaling. You&apos;d also have a contention pattern that maps to your real workload (active query count) instead of your app&apos;s worker count.&lt;/p&gt;
&lt;p&gt;This is what a connection pooler is. PgBouncer is the one most people use.&lt;/p&gt;
&lt;h2&gt;Act 2 — Three modes of multiplexing&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Session, transaction, statement: the spectrum of how aggressively you&apos;re willing to lie.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;5. Meet PgBouncer&lt;/h3&gt;
&lt;p&gt;PgBouncer is a single binary, written in C, single-threaded, event-driven, built on libevent. It speaks the Postgres wire protocol on both sides. To your application, it looks like a Postgres server. To the real Postgres, it looks like a bunch of clients. It sits in the middle and shuffles bytes back and forth, occasionally attaching and detaching client-to-server pairings according to whichever pooling mode you have configured.&lt;/p&gt;
&lt;p&gt;The numbers are striking. The PgBouncer process is small: a base of a few MB plus roughly 2 KB of bookkeeping per client connection (more once you turn on &lt;code&gt;max_prepared_statements&lt;/code&gt; tracking). A single node can hold many thousands of client connections; configurations of &lt;code&gt;max_client_conn = 10000&lt;/code&gt; are common in real deployments, with the practical ceiling set by file descriptors, TLS overhead, and the single-core CPU limit rather than memory. The per-query latency overhead is small (sub-millisecond in healthy LAN benchmarks), mostly because it does not parse SQL. It just looks at protocol messages, sees where one transaction ends and the next begins, and decides whether the current client/server pair should stay attached or get split apart.&lt;/p&gt;
&lt;p&gt;That last sentence is the entire concept. The pooling mode is the rule for when PgBouncer can detach a client from a server connection and reuse that server connection for someone else. There are three modes.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 700 360&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;PgBouncer sitting between many clients and few server connections&quot; width=&quot;100%&quot; height=&quot;auto&quot;&amp;gt;
&amp;lt;rect fill=&quot;#f4ede0&quot; x=&quot;0&quot; y=&quot;0&quot; width=&quot;700&quot; height=&quot;360&quot; rx=&quot;6&quot;/&amp;gt;
&amp;lt;text font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1a1815&quot; x=&quot;40&quot; y=&quot;30&quot; text-anchor=&quot;start&quot; font-weight=&quot;600&quot;&amp;gt;app workers&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;40&quot; y=&quot;46&quot; text-anchor=&quot;start&quot;&amp;gt;cheap proxy connections&amp;lt;/text&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;g fill=&quot;#fff&quot; stroke=&quot;#4a6f88&quot; stroke-width=&quot;1&quot;&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;65&quot;  width=&quot;80&quot; height=&quot;22&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;92&quot;  width=&quot;80&quot; height=&quot;22&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;119&quot; width=&quot;80&quot; height=&quot;22&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;146&quot; width=&quot;80&quot; height=&quot;22&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;173&quot; width=&quot;80&quot; height=&quot;22&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;200&quot; width=&quot;80&quot; height=&quot;22&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;227&quot; width=&quot;80&quot; height=&quot;22&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;254&quot; width=&quot;80&quot; height=&quot;22&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;281&quot; width=&quot;80&quot; height=&quot;22&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;308&quot; width=&quot;80&quot; height=&quot;22&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;80&quot; y=&quot;80&quot; text-anchor=&quot;middle&quot;&amp;gt;w01&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;80&quot; y=&quot;107&quot; text-anchor=&quot;middle&quot;&amp;gt;w02&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;80&quot; y=&quot;134&quot; text-anchor=&quot;middle&quot;&amp;gt;w03&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;80&quot; y=&quot;161&quot; text-anchor=&quot;middle&quot;&amp;gt;w04&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;80&quot; y=&quot;188&quot; text-anchor=&quot;middle&quot;&amp;gt;w05&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;80&quot; y=&quot;215&quot; text-anchor=&quot;middle&quot;&amp;gt;w06&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;80&quot; y=&quot;242&quot; text-anchor=&quot;middle&quot;&amp;gt;w07&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;80&quot; y=&quot;269&quot; text-anchor=&quot;middle&quot;&amp;gt;w08&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;80&quot; y=&quot;296&quot; text-anchor=&quot;middle&quot;&amp;gt;w09&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;80&quot; y=&quot;323&quot; text-anchor=&quot;middle&quot;&amp;gt;w10&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;260&quot; y=&quot;120&quot; width=&quot;180&quot; height=&quot;120&quot; rx=&quot;6&quot; fill=&quot;#fff&quot; stroke=&quot;#1a1815&quot; stroke-width=&quot;1.6&quot;/&amp;gt;
&amp;lt;text font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1a1815&quot; x=&quot;350&quot; y=&quot;148&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot;&amp;gt;PgBouncer&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;170&quot; text-anchor=&quot;middle&quot;&amp;gt;single process&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;186&quot; text-anchor=&quot;middle&quot;&amp;gt;single thread&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;202&quot; text-anchor=&quot;middle&quot;&amp;gt;~3 MB RAM&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;218&quot; text-anchor=&quot;middle&quot;&amp;gt;listens on 6432&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g stroke=&quot;#4a6f88&quot; fill=&quot;none&quot; stroke-width=&quot;0.8&quot;&amp;gt;
&amp;lt;path d=&quot;M 120 76  L 260 145&quot;/&amp;gt;
&amp;lt;path d=&quot;M 120 103 L 260 155&quot;/&amp;gt;
&amp;lt;path d=&quot;M 120 130 L 260 165&quot;/&amp;gt;
&amp;lt;path d=&quot;M 120 157 L 260 175&quot;/&amp;gt;
&amp;lt;path d=&quot;M 120 184 L 260 185&quot;/&amp;gt;
&amp;lt;path d=&quot;M 120 211 L 260 195&quot;/&amp;gt;
&amp;lt;path d=&quot;M 120 238 L 260 205&quot;/&amp;gt;
&amp;lt;path d=&quot;M 120 265 L 260 215&quot;/&amp;gt;
&amp;lt;path d=&quot;M 120 292 L 260 225&quot;/&amp;gt;
&amp;lt;path d=&quot;M 120 319 L 260 235&quot;/&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1a1815&quot; x=&quot;660&quot; y=&quot;30&quot; text-anchor=&quot;end&quot; font-weight=&quot;600&quot;&amp;gt;Postgres backends&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;660&quot; y=&quot;46&quot; text-anchor=&quot;end&quot;&amp;gt;expensive real connections&amp;lt;/text&amp;gt;
&amp;lt;g fill=&quot;#fff&quot; stroke=&quot;#6a7d63&quot; stroke-width=&quot;1.2&quot;&amp;gt;
&amp;lt;rect x=&quot;540&quot; y=&quot;140&quot; width=&quot;120&quot; height=&quot;28&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;rect x=&quot;540&quot; y=&quot;175&quot; width=&quot;120&quot; height=&quot;28&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;rect x=&quot;540&quot; y=&quot;210&quot; width=&quot;120&quot; height=&quot;28&quot; rx=&quot;3&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;600&quot; y=&quot;158&quot; text-anchor=&quot;middle&quot;&amp;gt;backend 1&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;600&quot; y=&quot;193&quot; text-anchor=&quot;middle&quot;&amp;gt;backend 2&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;600&quot; y=&quot;228&quot; text-anchor=&quot;middle&quot;&amp;gt;backend 3&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g stroke=&quot;#6a7d63&quot; fill=&quot;none&quot; stroke-width=&quot;1&quot;&amp;gt;
&amp;lt;path d=&quot;M 440 154 L 540 154&quot;/&amp;gt;
&amp;lt;path d=&quot;M 440 189 L 540 189&quot;/&amp;gt;
&amp;lt;path d=&quot;M 440 224 L 540 224&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;280&quot; text-anchor=&quot;middle&quot;&amp;gt;10 clients in&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;296&quot; text-anchor=&quot;middle&quot;&amp;gt;3 server connections out&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 2.&amp;lt;/strong&amp;gt; PgBouncer&apos;s job in one picture. Many cheap proxy connections on the client side, a small number of real Postgres backends on the server side, and a multiplexer in the middle deciding who gets which backend, when. The ratio (10:3 here, often 100:20 in production) is the whole point.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;h3&gt;6. Session pooling&lt;/h3&gt;
&lt;p&gt;Session pooling is the conservative default. A client connects to PgBouncer, PgBouncer hands them a real backend, and that backend belongs to that client until the client disconnects. From the application&apos;s point of view, this is identical to talking to Postgres directly.&lt;/p&gt;
&lt;p&gt;What does this buy you? Not multiplexing. If you have 200 app workers, you have 200 server connections. The pooler is not really pooling anything during steady-state operation. What it &lt;em&gt;does&lt;/em&gt; buy you is a few smaller wins. The connection setup cost is amortized: when a worker disconnects and another reconnects, the backend stays warm in PgBouncer&apos;s pool and gets reassigned without forking a new Postgres process. PgBouncer handles SCRAM authentication on the client side without holding open file descriptors during slow handshakes. And when Postgres restarts (for a parameter change, an HA failover, a scheduled maintenance), PgBouncer can reconnect on the back side while clients hold steady on the front side, which is genuinely useful for keeping your app from cascading.&lt;/p&gt;
&lt;p&gt;Session mode is the safe choice. Every Postgres feature works exactly as you&apos;d expect: prepared statements, temp tables, &lt;code&gt;LISTEN/NOTIFY&lt;/code&gt;, session-level advisory locks, &lt;code&gt;SET&lt;/code&gt;, search paths, the lot. There is essentially nothing you have to change in your application.&lt;/p&gt;
&lt;p&gt;It is also, for most modern web workloads, the wrong choice. The whole reason we started this conversation is that 200 idle backends costs real money. Session pooling does not save you from that. It saves you only from the connect/disconnect tax, which is real but secondary.&lt;/p&gt;
&lt;h3&gt;7. Transaction pooling&lt;/h3&gt;
&lt;p&gt;Transaction pooling is the mode that makes PgBouncer worth running. The rule is simple: a server backend is bound to a client only for the duration of a transaction. As soon as the client commits or rolls back, the backend goes back to the pool, and the next client that wants to start a transaction gets it.&lt;/p&gt;
&lt;p&gt;This is where the math changes. A web request typically takes 50 ms total but actually has the database connection in a transaction for maybe 5 ms of that. If you measure your real &quot;active in transaction&quot; time and divide by your total request time, you get a multiplier somewhere between 5x and 50x for typical workloads. That is your effective overcommit ratio. With transaction pooling, you can comfortably support 200 concurrent app workers on 20 server connections, because at any given instant only ~5% of them are actually mid-transaction.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 700 320&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Timeline showing the three pooling modes and when server connections are bound to clients&quot; width=&quot;100%&quot; height=&quot;auto&quot;&amp;gt;
&amp;lt;rect fill=&quot;#f4ede0&quot; x=&quot;0&quot; y=&quot;0&quot; width=&quot;700&quot; height=&quot;320&quot; rx=&quot;6&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;20&quot; width=&quot;14&quot; height=&quot;10&quot; fill=&quot;#6a7d63&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#1a1815&quot; x=&quot;60&quot; y=&quot;29&quot;&amp;gt;server bound to client&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;240&quot; y=&quot;20&quot; width=&quot;14&quot; height=&quot;10&quot; fill=&quot;#fff&quot; stroke=&quot;#1a1815&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#1a1815&quot; x=&quot;260&quot; y=&quot;29&quot;&amp;gt;server in pool, available&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1a1815&quot; x=&quot;40&quot; y=&quot;70&quot; font-weight=&quot;600&quot;&amp;gt;session&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;40&quot; y1=&quot;80&quot; x2=&quot;660&quot; y2=&quot;80&quot; stroke=&quot;#d9cfb9&quot;/&amp;gt;
&amp;lt;rect x=&quot;100&quot; y=&quot;74&quot; width=&quot;540&quot; height=&quot;14&quot; fill=&quot;#6a7d63&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;100&quot; y=&quot;105&quot;&amp;gt;connect&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;640&quot; y=&quot;105&quot; text-anchor=&quot;end&quot;&amp;gt;disconnect&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;370&quot; y=&quot;105&quot; text-anchor=&quot;middle&quot;&amp;gt;held the whole time, even when idle&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1a1815&quot; x=&quot;40&quot; y=&quot;160&quot; font-weight=&quot;600&quot;&amp;gt;transaction&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;40&quot; y1=&quot;170&quot; x2=&quot;660&quot; y2=&quot;170&quot; stroke=&quot;#d9cfb9&quot;/&amp;gt;
&amp;lt;rect x=&quot;100&quot; y=&quot;164&quot; width=&quot;60&quot; height=&quot;14&quot; fill=&quot;#6a7d63&quot;/&amp;gt;
&amp;lt;rect x=&quot;220&quot; y=&quot;164&quot; width=&quot;40&quot; height=&quot;14&quot; fill=&quot;#6a7d63&quot;/&amp;gt;
&amp;lt;rect x=&quot;320&quot; y=&quot;164&quot; width=&quot;80&quot; height=&quot;14&quot; fill=&quot;#6a7d63&quot;/&amp;gt;
&amp;lt;rect x=&quot;450&quot; y=&quot;164&quot; width=&quot;50&quot; height=&quot;14&quot; fill=&quot;#6a7d63&quot;/&amp;gt;
&amp;lt;rect x=&quot;560&quot; y=&quot;164&quot; width=&quot;70&quot; height=&quot;14&quot; fill=&quot;#6a7d63&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;130&quot; y=&quot;195&quot; text-anchor=&quot;middle&quot;&amp;gt;BEGIN…COMMIT&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;240&quot; y=&quot;195&quot; text-anchor=&quot;middle&quot;&amp;gt;tx&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;360&quot; y=&quot;195&quot; text-anchor=&quot;middle&quot;&amp;gt;tx&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;475&quot; y=&quot;195&quot; text-anchor=&quot;middle&quot;&amp;gt;tx&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;595&quot; y=&quot;195&quot; text-anchor=&quot;middle&quot;&amp;gt;tx&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;370&quot; y=&quot;215&quot; text-anchor=&quot;middle&quot;&amp;gt;in between, the backend is free for someone else&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1a1815&quot; x=&quot;40&quot; y=&quot;260&quot; font-weight=&quot;600&quot;&amp;gt;statement&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;40&quot; y1=&quot;270&quot; x2=&quot;660&quot; y2=&quot;270&quot; stroke=&quot;#d9cfb9&quot;/&amp;gt;
&amp;lt;g fill=&quot;#6a7d63&quot;&amp;gt;
&amp;lt;rect x=&quot;100&quot; y=&quot;264&quot; width=&quot;14&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;130&quot; y=&quot;264&quot; width=&quot;14&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;170&quot; y=&quot;264&quot; width=&quot;14&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;220&quot; y=&quot;264&quot; width=&quot;14&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;280&quot; y=&quot;264&quot; width=&quot;14&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;330&quot; y=&quot;264&quot; width=&quot;14&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;370&quot; y=&quot;264&quot; width=&quot;14&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;420&quot; y=&quot;264&quot; width=&quot;14&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;470&quot; y=&quot;264&quot; width=&quot;14&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;510&quot; y=&quot;264&quot; width=&quot;14&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;560&quot; y=&quot;264&quot; width=&quot;14&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;600&quot; y=&quot;264&quot; width=&quot;14&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;rect x=&quot;640&quot; y=&quot;264&quot; width=&quot;14&quot; height=&quot;14&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;370&quot; y=&quot;298&quot; text-anchor=&quot;middle&quot;&amp;gt;released after every single statement; multi-statement transactions forbidden&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 3.&amp;lt;/strong&amp;gt; The three modes, on the same timeline. Session holds a backend for the whole client lifetime. Transaction holds it only during &amp;lt;code&amp;gt;BEGIN…COMMIT&amp;lt;/code&amp;gt; and releases it in the gaps. Statement releases after every query, and refuses to let you have multi-statement transactions at all. As you go down the spectrum, multiplexing improves and Postgres compatibility shrinks.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;The catch is that transaction pooling lies to your application about how connections work. If you set a session variable in transaction A, and the next call you make happens in transaction B, transaction B might be on a different server backend, where the variable is unset. If you create a temp table in transaction A, it will not be visible in transaction B, because temp tables live in a backend&apos;s session and the next backend doesn&apos;t know about it. Anything that depends on Postgres&apos; notion of a &quot;session&quot; is now broken, because what your app thinks of as a session is no longer mapped onto a single backend&apos;s session. We&apos;ll come back to this. It&apos;s the bulk of what you have to think about when you adopt transaction pooling.&lt;/p&gt;
&lt;h3&gt;8. Statement pooling&lt;/h3&gt;
&lt;p&gt;Statement pooling is transaction pooling&apos;s wilder cousin. The server is released after every single statement, and multi-statement transactions are forbidden outright: if your client sends &lt;code&gt;BEGIN&lt;/code&gt;, PgBouncer returns an error.&lt;/p&gt;
&lt;p&gt;This is genuinely too aggressive for almost any application. The use case is something like a key-value lookup workload run from a tool that does not need transactions, where the cost of even briefly holding a backend across statements is higher than the cost of losing transaction semantics. PL/Proxy historical setups used it. Most modern apps should not.&lt;/p&gt;
&lt;p&gt;I&apos;m going to mostly ignore statement pooling for the rest of this piece. Its existence is mostly important because it shows you the spectrum: session is &quot;bind for the whole connection,&quot; transaction is &quot;bind for one transaction,&quot; statement is &quot;bind for one query.&quot; There&apos;s no rule that says PgBouncer has to stop there. Statement pooling shows you the limit case, where the server is held only while a single SQL command is in flight. Past that, you&apos;d be parsing SQL and rewriting it to push specific queries to specific backends, which is what middleware like Pgpool-II does, with all the complexity that entails.&lt;/p&gt;
&lt;h3&gt;9. The compatibility table&lt;/h3&gt;
&lt;p&gt;Here is a condensed compatibility chart, drawn from PgBouncer&apos;s docs and a few extra rows that bite people in practice. It&apos;s worth memorizing if you&apos;re going to run transaction pooling.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;session&lt;/th&gt;
&lt;th&gt;transaction&lt;/th&gt;
&lt;th&gt;statement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Multi-statement transactions&lt;/td&gt;
&lt;td&gt;works&lt;/td&gt;
&lt;td&gt;works&lt;/td&gt;
&lt;td&gt;forbidden&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SET&lt;/code&gt; / &lt;code&gt;RESET&lt;/code&gt; across queries&lt;/td&gt;
&lt;td&gt;works&lt;/td&gt;
&lt;td&gt;broken&lt;/td&gt;
&lt;td&gt;broken&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LISTEN&lt;/code&gt; (receiving)&lt;/td&gt;
&lt;td&gt;works&lt;/td&gt;
&lt;td&gt;broken&lt;/td&gt;
&lt;td&gt;broken&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NOTIFY&lt;/code&gt; (sending)&lt;/td&gt;
&lt;td&gt;works&lt;/td&gt;
&lt;td&gt;works&lt;/td&gt;
&lt;td&gt;broken&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Session-level advisory locks&lt;/td&gt;
&lt;td&gt;works&lt;/td&gt;
&lt;td&gt;broken&lt;/td&gt;
&lt;td&gt;broken&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transaction-level advisory locks&lt;/td&gt;
&lt;td&gt;works&lt;/td&gt;
&lt;td&gt;works&lt;/td&gt;
&lt;td&gt;broken&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Temporary tables&lt;/td&gt;
&lt;td&gt;works&lt;/td&gt;
&lt;td&gt;broken*&lt;/td&gt;
&lt;td&gt;broken*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WITH HOLD&lt;/code&gt; cursors&lt;/td&gt;
&lt;td&gt;works&lt;/td&gt;
&lt;td&gt;broken&lt;/td&gt;
&lt;td&gt;broken&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protocol prepared statements&lt;/td&gt;
&lt;td&gt;works&lt;/td&gt;
&lt;td&gt;works since 1.21&lt;/td&gt;
&lt;td&gt;works since 1.21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PREPARE&lt;/code&gt; / &lt;code&gt;EXECUTE&lt;/code&gt; SQL&lt;/td&gt;
&lt;td&gt;works&lt;/td&gt;
&lt;td&gt;broken&lt;/td&gt;
&lt;td&gt;broken&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SET LOCAL&lt;/code&gt; inside a transaction&lt;/td&gt;
&lt;td&gt;works&lt;/td&gt;
&lt;td&gt;works&lt;/td&gt;
&lt;td&gt;broken&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pg_dump&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;works&lt;/td&gt;
&lt;td&gt;broken&lt;/td&gt;
&lt;td&gt;broken&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logical replication connections&lt;/td&gt;
&lt;td&gt;since 1.23&lt;/td&gt;
&lt;td&gt;unsupported&lt;/td&gt;
&lt;td&gt;unsupported&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&quot;Broken&quot; here means &quot;may silently misbehave.&quot; Temp tables created inside a single transaction with &lt;code&gt;ON COMMIT DROP&lt;/code&gt; work fine, because they do not outlive the transaction. The asterisk on temp tables is for the common &lt;code&gt;ON COMMIT PRESERVE ROWS&lt;/code&gt; case, where the table is meant to live for the session. That&apos;s the one that breaks.&lt;/p&gt;
&lt;p&gt;Two of these have stories worth telling, because they&apos;re the ones that bite people most often. Prepared statements got a real fix in 2023. &lt;code&gt;SET&lt;/code&gt; still doesn&apos;t, and the workarounds are awkward.&lt;/p&gt;
&lt;h3&gt;10. Prepared statements: the asterisk that took ten years to remove&lt;/h3&gt;
&lt;p&gt;For most of PgBouncer&apos;s history, you couldn&apos;t use prepared statements in transaction mode at all. The reason is straightforward. A prepared statement in Postgres is named, and it lives in a specific backend&apos;s session memory. If your driver does &lt;code&gt;PREPARE q1 AS SELECT ...&lt;/code&gt; on backend A and then later does &lt;code&gt;EXECUTE q1&lt;/code&gt;, but PgBouncer has by now moved you to backend B, backend B has never heard of &lt;code&gt;q1&lt;/code&gt; and you get an error.&lt;/p&gt;
&lt;p&gt;The official advice for ten years was: turn off prepared statements at the driver level. JDBC users set &lt;code&gt;prepareThreshold=0&lt;/code&gt;. PHP/PDO users set &lt;code&gt;PDO::ATTR_EMULATE_PREPARES&lt;/code&gt;. Hibernate, ActiveRecord, and others have similar knobs. This works, but it costs you the parsing-and-planning savings that prepared statements were supposed to give you in the first place.&lt;/p&gt;
&lt;p&gt;PgBouncer 1.21, released in October 2023, finally added support for protocol-level named prepared statements in transaction pooling mode. The trick is clever. PgBouncer intercepts the prepare command from the client, gives the statement an internal name like &lt;code&gt;PGBOUNCER_1234&lt;/code&gt;, and tracks the mapping. When the same client (or any other client) later asks to &lt;code&gt;EXECUTE&lt;/code&gt; that statement on whatever backend they happen to land on, PgBouncer checks whether the backend already has the statement under the internal name. If it does, the statement runs. If it doesn&apos;t, PgBouncer prepares it on the fly, transparently, before forwarding the execute. The internal LRU cache of prepared statements per backend is bounded by the &lt;code&gt;max_prepared_statements&lt;/code&gt; setting.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 700 360&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Diagram showing how PgBouncer rewrites prepared statement names to internal identifiers shared across backends&quot; width=&quot;100%&quot; height=&quot;auto&quot;&amp;gt;
&amp;lt;rect fill=&quot;#f4ede0&quot; x=&quot;0&quot; y=&quot;0&quot; width=&quot;700&quot; height=&quot;360&quot; rx=&quot;6&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1a1815&quot; x=&quot;80&quot; y=&quot;30&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot;&amp;gt;client driver&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;20&quot; y=&quot;50&quot; width=&quot;160&quot; height=&quot;34&quot; rx=&quot;3&quot; fill=&quot;#fff&quot; stroke=&quot;#4a6f88&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#1a1815&quot; x=&quot;100&quot; y=&quot;72&quot; text-anchor=&quot;middle&quot;&amp;gt;Parse &quot;my_q&quot;: SELECT…&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;20&quot; y=&quot;100&quot; width=&quot;160&quot; height=&quot;34&quot; rx=&quot;3&quot; fill=&quot;#fff&quot; stroke=&quot;#4a6f88&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#1a1815&quot; x=&quot;100&quot; y=&quot;122&quot; text-anchor=&quot;middle&quot;&amp;gt;Execute &quot;my_q&quot; (args)&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;220&quot; y=&quot;40&quot; width=&quot;260&quot; height=&quot;160&quot; rx=&quot;5&quot; fill=&quot;#fff&quot; stroke=&quot;#1a1815&quot; stroke-width=&quot;1.4&quot;/&amp;gt;
&amp;lt;text font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1a1815&quot; x=&quot;350&quot; y=&quot;62&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot;&amp;gt;PgBouncer&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;84&quot; text-anchor=&quot;middle&quot;&amp;gt;global query cache&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;240&quot; y=&quot;96&quot; width=&quot;220&quot; height=&quot;32&quot; rx=&quot;3&quot; fill=&quot;#f1ebdc&quot; stroke=&quot;#b07a4a&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#1a1815&quot; x=&quot;350&quot; y=&quot;116&quot; text-anchor=&quot;middle&quot;&amp;gt;my_q  →  PGBOUNCER_42&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;148&quot; text-anchor=&quot;middle&quot;&amp;gt;prepares PGBOUNCER_42 on&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;164&quot; text-anchor=&quot;middle&quot;&amp;gt;whichever backend the client&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;180&quot; text-anchor=&quot;middle&quot;&amp;gt;lands on, transparently&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g stroke=&quot;#4a4742&quot; fill=&quot;none&quot; stroke-width=&quot;1&quot;&amp;gt;
&amp;lt;path d=&quot;M 180 67 L 220 90&quot;/&amp;gt;
&amp;lt;path d=&quot;M 180 117 L 220 130&quot;/&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;540&quot; y=&quot;65&quot; width=&quot;140&quot; height=&quot;50&quot; rx=&quot;3&quot; fill=&quot;#fff&quot; stroke=&quot;#6a7d63&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#1a1815&quot; x=&quot;610&quot; y=&quot;82&quot; text-anchor=&quot;middle&quot;&amp;gt;backend 1&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;610&quot; y=&quot;98&quot; text-anchor=&quot;middle&quot;&amp;gt;PGBOUNCER_42 ✓&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;540&quot; y=&quot;135&quot; width=&quot;140&quot; height=&quot;50&quot; rx=&quot;3&quot; fill=&quot;#fff&quot; stroke=&quot;#6a7d63&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#1a1815&quot; x=&quot;610&quot; y=&quot;152&quot; text-anchor=&quot;middle&quot;&amp;gt;backend 2&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;610&quot; y=&quot;168&quot; text-anchor=&quot;middle&quot;&amp;gt;PGBOUNCER_42 ✓&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;rect x=&quot;540&quot; y=&quot;205&quot; width=&quot;140&quot; height=&quot;50&quot; rx=&quot;3&quot; fill=&quot;#fff&quot; stroke=&quot;#6a7d63&quot; stroke-width=&quot;1.2&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#1a1815&quot; x=&quot;610&quot; y=&quot;222&quot; text-anchor=&quot;middle&quot;&amp;gt;backend 3&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;610&quot; y=&quot;238&quot; text-anchor=&quot;middle&quot;&amp;gt;PGBOUNCER_42 ✓&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;g stroke=&quot;#6a7d63&quot; fill=&quot;none&quot; stroke-width=&quot;1&quot;&amp;gt;
&amp;lt;path d=&quot;M 480 110 L 540 90&quot;/&amp;gt;
&amp;lt;path d=&quot;M 480 130 L 540 160&quot;/&amp;gt;
&amp;lt;path d=&quot;M 480 150 L 540 230&quot;/&amp;gt;
&amp;lt;/g&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;290&quot; text-anchor=&quot;middle&quot;&amp;gt;Same prepared SQL string is interned once. Each backend gets it&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;306&quot; text-anchor=&quot;middle&quot;&amp;gt;prepared on first use, cached as PGBOUNCER_42, reused thereafter.&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;322&quot; text-anchor=&quot;middle&quot;&amp;gt;The client thinks it&apos;s still talking about my_q. Everything stays consistent.&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 4.&amp;lt;/strong&amp;gt; How PgBouncer 1.21+ handles prepared statements under transaction pooling. The client&apos;s name is mapped to a PgBouncer-internal name. The same SQL string used by many clients gets a single internal identity. Each backend lazily prepares the statement on first use and caches it, bounded by &amp;lt;code&amp;gt;max_prepared_statements&amp;lt;/code&amp;gt;.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;As of PgBouncer 1.24.0 (January 2025), prepared statement support is enabled by default with &lt;code&gt;max_prepared_statements = 200&lt;/code&gt;. If you&apos;re on a fresh deploy from 2025 onward, you have it for free. If you&apos;re on an older config, set the value yourself; reasonable values are 100 to 500 depending on how many distinct prepared statements your app actually uses. Higher means more memory per backend, both in PgBouncer&apos;s tracking and in Postgres&apos; plan cache. The Tiger Data team recommends matching your &lt;code&gt;max_prepared_statements&lt;/code&gt; to the count of distinct statements your ORM emits, so the cache fits exactly.&lt;/p&gt;
&lt;p&gt;Two caveats remain. First, this only applies to &lt;em&gt;protocol-level&lt;/em&gt; prepared statements, the ones your driver sends as &lt;code&gt;Parse&lt;/code&gt;/&lt;code&gt;Bind&lt;/code&gt;/&lt;code&gt;Execute&lt;/code&gt; messages. SQL-level &lt;code&gt;PREPARE foo AS ...&lt;/code&gt; followed by &lt;code&gt;EXECUTE foo&lt;/code&gt; is still unsupported, because PgBouncer doesn&apos;t parse SQL and can&apos;t see those statements coming. If your app uses raw &lt;code&gt;PREPARE&lt;/code&gt;, you have to stop. If your app uses an ORM or any modern driver, it&apos;s almost certainly using the protocol level, and you&apos;re fine.&lt;/p&gt;
&lt;p&gt;Second, and this is irritating: client-side &lt;code&gt;DEALLOCATE&lt;/code&gt; for individual prepared statements is still not transparently rewritten. &lt;code&gt;DEALLOCATE ALL&lt;/code&gt; and &lt;code&gt;DISCARD ALL&lt;/code&gt; are handled (added in PgBouncer 1.22, January 2024) and correctly clear PgBouncer&apos;s tracking. But a single &lt;code&gt;DEALLOCATE my_q&lt;/code&gt; goes straight through to whichever backend the client happens to be on, which may not have the statement under that name, throwing an error. The protocol-level &lt;em&gt;Close&lt;/em&gt; message for prepared statements has been part of Postgres&apos; wire protocol for years; what&apos;s new is libpq 17 (Postgres 17, September 2024) exposing it via &lt;code&gt;PQclosePrepared&lt;/code&gt;, so drivers can finally deallocate without sending raw SQL. Driver adoption is uneven. The pragmatic fix in older drivers is to disable client-side deallocation and trust PgBouncer&apos;s LRU to evict old statements. PHP/PDO needs PHP 8.4+ and libpq 17 to be compatible with PgBouncer&apos;s prepared-statement support; for older PHP, set &lt;code&gt;PDO::ATTR_EMULATE_PREPARES = true&lt;/code&gt;. Most other modern drivers (psycopg3, JDBC, Go&apos;s pgx) work but each has their own knob for controlling deallocation behavior. Verify before you ship.&lt;/p&gt;
&lt;h3&gt;11. The other things that break, and what to do about them&lt;/h3&gt;
&lt;p&gt;Prepared statements got fixed. Most of the other items on the compatibility list didn&apos;t, and probably won&apos;t, because the underlying issue is fundamental to transaction pooling: any feature that depends on persistent session state across transactions is incompatible with a model where transactions get scattered across different backends. You can&apos;t paper over that without losing the multiplexing.&lt;/p&gt;
&lt;p&gt;The list of practical things this means:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SET commands.&lt;/strong&gt; &lt;code&gt;SET search_path = &apos;tenant_42&apos;&lt;/code&gt; at the start of a request will succeed, but as soon as the request commits and starts a new transaction, the next backend you land on doesn&apos;t have that &lt;code&gt;search_path&lt;/code&gt;. The fix is &lt;code&gt;SET LOCAL&lt;/code&gt;, which is scoped to the current transaction. If you need session-scoped settings (multi-tenant search paths are the most common case), you have to either set them on every transaction, or set them at the role level with &lt;code&gt;ALTER ROLE ... SET search_path = ...&lt;/code&gt;, or use parameters PgBouncer specifically tracks across transactions. The default tracked set is small: &lt;code&gt;client_encoding&lt;/code&gt;, &lt;code&gt;application_name&lt;/code&gt;, &lt;code&gt;DateStyle&lt;/code&gt;, &lt;code&gt;TimeZone&lt;/code&gt;, &lt;code&gt;standard_conforming_strings&lt;/code&gt;, and (since 1.20) &lt;code&gt;IntervalStyle&lt;/code&gt;. You can extend this list with &lt;code&gt;track_extra_parameters&lt;/code&gt;, but only for parameters that Postgres reports back to the client (the GUC must be marked &lt;code&gt;GUC_REPORT&lt;/code&gt;). For &lt;code&gt;search_path&lt;/code&gt; specifically, this works on Citus 12+ and on Postgres 18, both of which mark &lt;code&gt;search_path&lt;/code&gt; as reportable. On older Postgres versions, you cannot track &lt;code&gt;search_path&lt;/code&gt; through PgBouncer no matter what you set; see the CVE note in the auth section for why this matters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;statement_timeout, the cursed one.&lt;/strong&gt; This is the bug that JP Camara wrote up and it&apos;s worth understanding because it bites people who think they understand the rules. If you set &lt;code&gt;statement_timeout&lt;/code&gt; at the session level (&lt;code&gt;SET statement_timeout = &apos;5s&apos;&lt;/code&gt;), and you set it on a backend that gets returned to the pool with that value still set, the next client to grab that backend inherits the 5-second timeout. They didn&apos;t ask for it. They might be a long-running background job. They will get killed at 5 seconds and never know why. This is solved by &lt;code&gt;server_reset_query = DISCARD ALL&lt;/code&gt; in session pooling, which resets everything. But in transaction pooling mode, &lt;code&gt;server_reset_query&lt;/code&gt; is not run by default, because the assumption is &quot;you weren&apos;t doing anything session-y in the first place.&quot; If you set &lt;code&gt;statement_timeout&lt;/code&gt; at the session level inside transaction pooling, you are leaking it to other clients. The fix is either to use &lt;code&gt;SET LOCAL statement_timeout&lt;/code&gt; inside an explicit transaction, or to set the timeout at the role level with &lt;code&gt;ALTER ROLE web_app SET statement_timeout = &apos;5s&apos;&lt;/code&gt; so it applies on connection establishment and never needs to change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LISTEN / NOTIFY.&lt;/strong&gt; &lt;code&gt;LISTEN&lt;/code&gt; registers an interest on a specific backend. In transaction pooling, you&apos;ll only receive notifications when you happen to land on that exact backend, which is essentially never. You can keep a separate direct connection (port 5432) for the LISTEN side and use the pooler for everything else, which works but undermines the pooling savings. For real pub/sub, use a real pub/sub system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Session-level advisory locks.&lt;/strong&gt; Worse than the others. &lt;code&gt;pg_advisory_lock(123)&lt;/code&gt; takes a lock that lives for the &lt;em&gt;backend&apos;s&lt;/em&gt; session, not the client&apos;s transaction. In transaction pooling, your transaction ends at &lt;code&gt;COMMIT&lt;/code&gt;, but the backend keeps running, holding your lock. Whoever lands on that backend next inherits the lock — meaning they can silently block on it, or release it themselves with an unrelated &lt;code&gt;pg_advisory_unlock(123)&lt;/code&gt;, or step on each other&apos;s locking logic in ways that look impossible from the application side. Use &lt;code&gt;pg_advisory_xact_lock(123)&lt;/code&gt; if your locking scope fits inside a transaction, or use a real distributed lock if it doesn&apos;t.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Temporary tables that outlive a transaction.&lt;/strong&gt; A temp table created with &lt;code&gt;ON COMMIT DROP&lt;/code&gt; inside a single transaction works fine in transaction pooling. &lt;code&gt;ON COMMIT PRESERVE ROWS&lt;/code&gt; (the default) creates a session-scoped temp table that won&apos;t be there next transaction. Most ORMs don&apos;t use temp tables, so this is mainly a problem for analytics tools and ETL.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;pg_dump.&lt;/strong&gt; Don&apos;t run pg_dump through PgBouncer. It expects session-level state and behaves badly. Use a direct connection for backups, period.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WITH HOLD cursors and LOAD.&lt;/strong&gt; Both session-y. Both broken. Both rare enough that most apps don&apos;t notice.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The mental model that makes this all easier.&lt;/strong&gt; Stop thinking of an app&apos;s connection to the database as a long-running thing. Start thinking of it as a request for a transaction. Each transaction is a brief encounter with some backend, and after it ends, you have no relationship to that backend. Anything you want to persist either has to be small (and PgBouncer-tracked, like &lt;code&gt;application_name&lt;/code&gt;), or has to live somewhere that isn&apos;t a backend&apos;s memory: the database itself, your app&apos;s memory, Redis, whatever. As soon as you internalize this, the gotchas stop being surprises and start being obvious consequences of the pooling rules.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Checkpoint: what you know now.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Postgres uses one OS process per connection. Each connection costs a few MB of private memory plus scheduler and snapshot overhead, and the connect-time fork/TLS dance is multiple milliseconds.&lt;/p&gt;
&lt;p&gt;PgBouncer is a single-threaded libevent-based proxy that multiplexes many client connections onto fewer real Postgres backends. It has three modes: session (one backend per client lifetime, mostly safe), transaction (backend bound only during &lt;code&gt;BEGIN…COMMIT&lt;/code&gt;, the productive default), and statement (released after each query, too aggressive for normal apps).&lt;/p&gt;
&lt;p&gt;Transaction mode breaks any feature that depends on session state spanning transactions. The most-bitten items are &lt;code&gt;SET&lt;/code&gt;, &lt;code&gt;LISTEN&lt;/code&gt;, session-scoped advisory locks, persistent temp tables, and statement-level &lt;code&gt;PREPARE&lt;/code&gt;. Protocol-level prepared statements work as of 1.21 (2023), enabled by default since 1.24 (January 2025).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Act 3 — Running it for real&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Sizing, auth, deployment topology, and the small operational decisions that determine whether you sleep through the night.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;12. Sizing the pool&lt;/h3&gt;
&lt;p&gt;This is the question I get asked most often, and the one that most blog posts answer poorly. The honest answer is: do the math, and the math starts at the database, not the app.&lt;/p&gt;
&lt;p&gt;Step one: figure out how many concurrent transactions Postgres can actually handle. There&apos;s no formula, but a useful starting point is &quot;two to four times the number of CPU cores.&quot; A 16-core database server can probably comfortably run 32 to 64 concurrent active transactions. Past that, you&apos;re queueing on CPU or I/O, and adding more concurrent transactions makes everything slower without making throughput higher. This is your ceiling.&lt;/p&gt;
&lt;p&gt;Step two: pick a &lt;code&gt;default_pool_size&lt;/code&gt; below that ceiling, leaving headroom. If your ceiling is 50, set the pool size to 20 to 30. The pool size is per &lt;em&gt;(database, user)&lt;/em&gt; pair: if you have one app user hitting one database, that&apos;s the whole pool. If you have multiple roles or multiple databases, those are separate pools that share the database&apos;s overall connection budget.&lt;/p&gt;
&lt;p&gt;Step three: pick a &lt;code&gt;max_client_conn&lt;/code&gt; that&apos;s much larger than the pool size. This is the cap on how many clients can connect to PgBouncer at all. Set it to your worst-case scenario: total worker count across all your app servers, plus headroom. 1,000 to 5,000 is normal. The whole reason you have PgBouncer is so that this number can be much larger than your pool size.&lt;/p&gt;
&lt;p&gt;Step four: set &lt;code&gt;max_db_connections&lt;/code&gt; as a safety cap. This is the absolute upper bound on real Postgres connections from this PgBouncer to this database, regardless of pool counts. Set it slightly below your Postgres &lt;code&gt;max_connections&lt;/code&gt;, leaving room for direct admin sessions and replication.&lt;/p&gt;
&lt;p&gt;There&apos;s a more analytical formulation that&apos;s worth knowing about, even if you don&apos;t use it. The pool size you actually need is roughly the peak rate of new transactions multiplied by the average transaction duration. If you&apos;re processing 500 transactions per second and each takes 20 ms, that&apos;s 500 × 0.02 = 10 active transactions on average. Round up generously, because the average hides spikes. Pool size of 20 is a reasonable starting point. This is Little&apos;s Law applied to transactions.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# A reasonable starting pgbouncer.ini for a single-tenant web app

[databases]
appdb = host=db.internal port=5432 dbname=appdb

[pgbouncer]
listen_addr     = 0.0.0.0
listen_port     = 6432
auth_type       = scram-sha-256
auth_file       = /etc/pgbouncer/userlist.txt

pool_mode             = transaction
max_client_conn       = 2000
default_pool_size     = 25
min_pool_size         = 5
reserve_pool_size     = 5
reserve_pool_timeout  = 3
max_db_connections    = 80
max_prepared_statements = 200

server_idle_timeout   = 600
server_lifetime       = 3600
query_wait_timeout    = 60

ignore_startup_parameters = extra_float_digits,options
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two settings deserve a footnote. &lt;code&gt;reserve_pool_size&lt;/code&gt; and &lt;code&gt;reserve_pool_timeout&lt;/code&gt; are PgBouncer&apos;s burst valve. If the regular pool is exhausted and clients have been waiting for more than &lt;code&gt;reserve_pool_timeout&lt;/code&gt; seconds, PgBouncer can open up to &lt;code&gt;reserve_pool_size&lt;/code&gt; additional backends to absorb the spike. Set this small. It&apos;s a release valve, not a primary sizing knob. If your reserve pool is firing constantly, your &lt;code&gt;default_pool_size&lt;/code&gt; is too small.&lt;/p&gt;
&lt;p&gt;And &lt;code&gt;min_pool_size&lt;/code&gt; tells PgBouncer to keep this many connections warm even when nothing is using them. The classic use case is avoiding cold-start fork latency on the first request after a quiet period. Don&apos;t set it too high; idle warmed connections are still real backends consuming real memory.&lt;/p&gt;
&lt;h3&gt;13. Where to put PgBouncer&lt;/h3&gt;
&lt;p&gt;There are three reasonable places to deploy PgBouncer, and the choice has real consequences.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On the database host.&lt;/strong&gt; One PgBouncer process running on the same machine as Postgres, listening on port 6432. This is the simplest setup and what most managed providers do (Azure Postgres, Crunchy Bridge, PlanetScale, Timescale Cloud). Latency between the pooler and the database is sub-millisecond because they&apos;re on a Unix socket or loopback. The downside is that if the database host falls over, the pooler falls over with it. Failover requires the standby to also have PgBouncer running.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On each app host (sidecar pattern).&lt;/strong&gt; One PgBouncer per application server, listening on localhost. Each app&apos;s connection goes to its local PgBouncer over a Unix socket. The PgBouncers all then connect across the network to Postgres. Pros: no single point of failure on the pooler tier. Cons: you have N PgBouncers, each maintaining their own pool, so the total connection budget is harder to reason about. If each app host has a pool of 25 and you have 10 hosts, you&apos;ve got 250 server connections, not 25.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On a dedicated pooler tier.&lt;/strong&gt; A small number (2 to 4) of dedicated PgBouncer hosts behind a load balancer or VIP, fed by all app servers. The setup PgBouncer&apos;s docs explicitly support uses Linux&apos;s &lt;code&gt;SO_REUSEPORT&lt;/code&gt; with multiple PgBouncer processes listening on the same port on the same host, which gives you parallelism without breaking the single-threaded model. This is the right pattern when your app servers fluctuate but your pooler tier should stay stable. It&apos;s also the most operationally complex.&lt;/p&gt;
&lt;p&gt;For most small to medium apps, &quot;on the database host&quot; is the answer and worrying about the others is premature. For larger systems, the sidecar pattern is the most resilient if you can do the math on total connections. The dedicated tier is where the very high-throughput shops end up.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 700 320&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Three deployment topologies: pooler on the database host, pooler as a sidecar, and a dedicated pooler tier&quot; width=&quot;100%&quot; height=&quot;auto&quot;&amp;gt;
&amp;lt;rect fill=&quot;#f4ede0&quot; x=&quot;0&quot; y=&quot;0&quot; width=&quot;700&quot; height=&quot;320&quot; rx=&quot;6&quot;/&amp;gt;
&amp;lt;text font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1a1815&quot; x=&quot;120&quot; y=&quot;30&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot;&amp;gt;on db host&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;60&quot; y=&quot;50&quot; width=&quot;120&quot; height=&quot;50&quot; rx=&quot;3&quot; fill=&quot;#fff&quot; stroke=&quot;#4a6f88&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#1a1815&quot; x=&quot;120&quot; y=&quot;80&quot; text-anchor=&quot;middle&quot;&amp;gt;app servers&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;60&quot; y=&quot;155&quot; width=&quot;120&quot; height=&quot;100&quot; rx=&quot;3&quot; fill=&quot;#fff&quot; stroke=&quot;#1a1815&quot; stroke-width=&quot;1.4&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#1a1815&quot; x=&quot;120&quot; y=&quot;180&quot; text-anchor=&quot;middle&quot;&amp;gt;db host&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;74&quot; y=&quot;190&quot; width=&quot;92&quot; height=&quot;22&quot; rx=&quot;3&quot; fill=&quot;#f1ebdc&quot; stroke=&quot;#b07a4a&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#1a1815&quot; x=&quot;120&quot; y=&quot;206&quot; text-anchor=&quot;middle&quot;&amp;gt;PgBouncer&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;74&quot; y=&quot;220&quot; width=&quot;92&quot; height=&quot;22&quot; rx=&quot;3&quot; fill=&quot;#fff&quot; stroke=&quot;#6a7d63&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#1a1815&quot; x=&quot;120&quot; y=&quot;236&quot; text-anchor=&quot;middle&quot;&amp;gt;postgres&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;120&quot; y1=&quot;100&quot; x2=&quot;120&quot; y2=&quot;155&quot; stroke=&quot;#4a4742&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;120&quot; y=&quot;285&quot; text-anchor=&quot;middle&quot;&amp;gt;simplest, single-host risk&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1a1815&quot; x=&quot;350&quot; y=&quot;30&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot;&amp;gt;sidecar&amp;lt;/text&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;rect x=&quot;280&quot; y=&quot;50&quot; width=&quot;60&quot; height=&quot;60&quot; rx=&quot;3&quot; fill=&quot;#fff&quot; stroke=&quot;#4a6f88&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;rect x=&quot;288&quot; y=&quot;80&quot; width=&quot;44&quot; height=&quot;22&quot; rx=&quot;2&quot; fill=&quot;#f1ebdc&quot; stroke=&quot;#b07a4a&quot; stroke-width=&quot;0.8&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; fill=&quot;#1a1815&quot; x=&quot;310&quot; y=&quot;68&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot;&amp;gt;app1&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; fill=&quot;#1a1815&quot; x=&quot;310&quot; y=&quot;96&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot;&amp;gt;PgB&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;rect x=&quot;350&quot; y=&quot;50&quot; width=&quot;60&quot; height=&quot;60&quot; rx=&quot;3&quot; fill=&quot;#fff&quot; stroke=&quot;#4a6f88&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;rect x=&quot;358&quot; y=&quot;80&quot; width=&quot;44&quot; height=&quot;22&quot; rx=&quot;2&quot; fill=&quot;#f1ebdc&quot; stroke=&quot;#b07a4a&quot; stroke-width=&quot;0.8&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; fill=&quot;#1a1815&quot; x=&quot;380&quot; y=&quot;68&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot;&amp;gt;app2&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; fill=&quot;#1a1815&quot; x=&quot;380&quot; y=&quot;96&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot;&amp;gt;PgB&amp;lt;/text&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/g&amp;gt;
&amp;lt;rect x=&quot;290&quot; y=&quot;170&quot; width=&quot;120&quot; height=&quot;50&quot; rx=&quot;3&quot; fill=&quot;#fff&quot; stroke=&quot;#6a7d63&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#1a1815&quot; x=&quot;350&quot; y=&quot;200&quot; text-anchor=&quot;middle&quot;&amp;gt;postgres&amp;lt;/text&amp;gt;
&amp;lt;g stroke=&quot;#4a4742&quot; fill=&quot;none&quot; stroke-width=&quot;1&quot;&amp;gt;
&amp;lt;path d=&quot;M 310 110 L 320 170&quot;/&amp;gt;
&amp;lt;path d=&quot;M 380 110 L 380 170&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;285&quot; text-anchor=&quot;middle&quot;&amp;gt;resilient, harder to reason&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;350&quot; y=&quot;300&quot; text-anchor=&quot;middle&quot;&amp;gt;about total connections&amp;lt;/text&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;text font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1a1815&quot; x=&quot;580&quot; y=&quot;30&quot; text-anchor=&quot;middle&quot; font-weight=&quot;600&quot;&amp;gt;dedicated tier&amp;lt;/text&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;rect x=&quot;510&quot; y=&quot;50&quot; width=&quot;60&quot; height=&quot;40&quot; rx=&quot;3&quot; fill=&quot;#fff&quot; stroke=&quot;#4a6f88&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; fill=&quot;#1a1815&quot; x=&quot;540&quot; y=&quot;76&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot;&amp;gt;app1&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;580&quot; y=&quot;50&quot; width=&quot;60&quot; height=&quot;40&quot; rx=&quot;3&quot; fill=&quot;#fff&quot; stroke=&quot;#4a6f88&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; fill=&quot;#1a1815&quot; x=&quot;610&quot; y=&quot;76&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot;&amp;gt;app2&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;rect x=&quot;510&quot; y=&quot;125&quot; width=&quot;60&quot; height=&quot;40&quot; rx=&quot;3&quot; fill=&quot;#f1ebdc&quot; stroke=&quot;#b07a4a&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; fill=&quot;#1a1815&quot; x=&quot;540&quot; y=&quot;150&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot;&amp;gt;PgB&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;580&quot; y=&quot;125&quot; width=&quot;60&quot; height=&quot;40&quot; rx=&quot;3&quot; fill=&quot;#f1ebdc&quot; stroke=&quot;#b07a4a&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; fill=&quot;#1a1815&quot; x=&quot;610&quot; y=&quot;150&quot; text-anchor=&quot;middle&quot; font-size=&quot;10&quot;&amp;gt;PgB&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;rect x=&quot;510&quot; y=&quot;200&quot; width=&quot;130&quot; height=&quot;40&quot; rx=&quot;3&quot; fill=&quot;#fff&quot; stroke=&quot;#6a7d63&quot; stroke-width=&quot;1&quot;/&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#1a1815&quot; x=&quot;575&quot; y=&quot;225&quot; text-anchor=&quot;middle&quot;&amp;gt;postgres&amp;lt;/text&amp;gt;
&amp;lt;g stroke=&quot;#4a4742&quot; fill=&quot;none&quot; stroke-width=&quot;1&quot;&amp;gt;
&amp;lt;path d=&quot;M 540 90 L 540 125&quot;/&amp;gt;
&amp;lt;path d=&quot;M 610 90 L 610 125&quot;/&amp;gt;
&amp;lt;path d=&quot;M 540 165 L 575 200&quot;/&amp;gt;
&amp;lt;path d=&quot;M 610 165 L 575 200&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;575&quot; y=&quot;285&quot; text-anchor=&quot;middle&quot;&amp;gt;most operationally&amp;lt;/text&amp;gt;
&amp;lt;text font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot; fill=&quot;#4a4742&quot; x=&quot;575&quot; y=&quot;300&quot; text-anchor=&quot;middle&quot;&amp;gt;flexible, most complex&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 5.&amp;lt;/strong&amp;gt; Three deployment topologies. The first one is what most managed Postgres providers do for you and is the right starting point. The third is what shops with high enough scale to justify a dedicated pooler tier do, often with &amp;lt;code&amp;gt;SO_REUSEPORT&amp;lt;/code&amp;gt;-based parallelism on each pooler host.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;h3&gt;14. Authentication, and the auth_query trick&lt;/h3&gt;
&lt;p&gt;By default, PgBouncer keeps its own user list in a file (&lt;code&gt;userlist.txt&lt;/code&gt;), with one line per user containing the username and a password (plain text, MD5, or SCRAM secret). This is fine for tiny setups. For anything with more than a handful of users, or where users get added dynamically, you do not want to maintain a parallel password file in sync with Postgres&apos; own auth.&lt;/p&gt;
&lt;p&gt;The right pattern is &lt;code&gt;auth_query&lt;/code&gt;. You give PgBouncer a single privileged user (call it &lt;code&gt;pgbouncer&lt;/code&gt;), and you tell it to look up other users&apos; password hashes by querying Postgres directly. The setup looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- In Postgres, once per database:
CREATE ROLE pgbouncer LOGIN PASSWORD &apos;...&apos;;

CREATE SCHEMA IF NOT EXISTS pgbouncer;
REVOKE ALL ON SCHEMA pgbouncer FROM PUBLIC;
GRANT USAGE ON SCHEMA pgbouncer TO pgbouncer;

CREATE OR REPLACE FUNCTION pgbouncer.get_auth(p_username TEXT)
RETURNS TABLE(username TEXT, password TEXT)
LANGUAGE sql
SECURITY DEFINER
SET search_path = pg_catalog, pg_temp
AS $$
    SELECT rolname::TEXT, rolpassword::TEXT
    FROM pg_catalog.pg_authid
    WHERE rolname = p_username
      AND rolcanlogin
      AND NOT rolsuper
      AND (rolvaliduntil IS NULL OR rolvaliduntil &amp;gt; CURRENT_TIMESTAMP);
$$;

REVOKE ALL ON FUNCTION pgbouncer.get_auth(TEXT) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION pgbouncer.get_auth(TEXT) TO pgbouncer;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# In pgbouncer.ini:
auth_type  = scram-sha-256
auth_user  = pgbouncer
auth_query = SELECT username, password FROM pgbouncer.get_auth($1)

# userlist.txt now only needs the pgbouncer user itself:
&quot;pgbouncer&quot; &quot;SCRAM-SHA-256$4096:...&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now any non-superuser role with login privilege can authenticate through PgBouncer without ever appearing in PgBouncer&apos;s own files. Adding a user is a single Postgres &lt;code&gt;CREATE ROLE&lt;/code&gt;; rotating a password is a single &lt;code&gt;ALTER ROLE&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Two things worth flagging, both of which have shown up as real CVEs in 2025. First, the &lt;code&gt;SET search_path = pg_catalog, pg_temp&lt;/code&gt; line in the function definition is not optional. Without it, an attacker can hijack the function&apos;s name resolution by getting the right object to live earlier in the search path. CVE-2025-12819, fixed in PgBouncer 1.25.1 (December 2025), was a real-world example. When &lt;code&gt;track_extra_parameters&lt;/code&gt; includes &lt;code&gt;search_path&lt;/code&gt; and &lt;code&gt;auth_user&lt;/code&gt; is configured, a malicious &lt;code&gt;search_path&lt;/code&gt; in the client&apos;s StartupMessage propagates into the auth-time &lt;code&gt;auth_query&lt;/code&gt;. PgBouncer&apos;s shipped default query contained an unqualified operator (&lt;code&gt;&amp;lt;&lt;/code&gt;), which the attacker&apos;s schema could redefine, executing arbitrary SQL during authentication. The hardened example above pins the search path inside the function and schema-qualifies &lt;code&gt;pg_authid&lt;/code&gt;, which neutralizes the attack. Always schema-qualify your catalog references in &lt;code&gt;SECURITY DEFINER&lt;/code&gt; functions, and always pin the search path.&lt;/p&gt;
&lt;p&gt;Second, notice the &lt;code&gt;(rolvaliduntil IS NULL OR rolvaliduntil &amp;gt; CURRENT_TIMESTAMP)&lt;/code&gt; clause. This is also not optional. CVE-2025-2291, fixed in PgBouncer 1.24.1 (April 2025), was the embarrassing case where the &lt;em&gt;shipped default&lt;/em&gt; &lt;code&gt;auth_query&lt;/code&gt; didn&apos;t check &lt;code&gt;VALID UNTIL&lt;/code&gt;, so an expired password would still authenticate through the pooler even though it wouldn&apos;t authenticate against Postgres directly. The default was fixed; if you wrote a custom &lt;code&gt;auth_query&lt;/code&gt; before April 2025, double-check that you&apos;re filtering on &lt;code&gt;rolvaliduntil&lt;/code&gt;. The example above does. Many older blog post examples don&apos;t.&lt;/p&gt;
&lt;p&gt;Third, end-to-end SCRAM authentication is delicate. PgBouncer can pass SCRAM through to Postgres, but only when the SCRAM secrets in PgBouncer&apos;s userlist exactly match Postgres&apos; (same salt, same iterations, not just the same password). Otherwise PgBouncer has to re-authenticate to Postgres on a separate channel, which introduces its own subtleties around credentials. For most setups, &lt;code&gt;auth_query&lt;/code&gt; with a single privileged &lt;code&gt;pgbouncer&lt;/code&gt; role is simpler and safer than trying to mirror SCRAM secrets exactly.&lt;/p&gt;
&lt;h3&gt;15. Watching it run&lt;/h3&gt;
&lt;p&gt;PgBouncer has a special database called &lt;code&gt;pgbouncer&lt;/code&gt; that you can connect to (with an admin user) and run a small set of &lt;code&gt;SHOW&lt;/code&gt; commands. They&apos;re how you understand what&apos;s actually happening.&lt;/p&gt;
&lt;p&gt;The two most useful are &lt;code&gt;SHOW POOLS&lt;/code&gt; and &lt;code&gt;SHOW STATS&lt;/code&gt;. &lt;code&gt;SHOW POOLS&lt;/code&gt; tells you, for each (database, user) pool, how many client connections are active or waiting, how many server connections are in each state (active, idle, used, tested), and what the pool&apos;s mode is. &lt;code&gt;SHOW STATS&lt;/code&gt; gives you per-database counters: total transactions, total queries, total bytes, and the average wait time for clients to be assigned a server connection. That last number is the one that tells you whether your sizing is right.&lt;/p&gt;
&lt;p&gt;Here&apos;s what I look at first when something is wrong:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cl_waiting&lt;/code&gt; in &lt;code&gt;SHOW POOLS&lt;/code&gt;: how many clients are queued, waiting for a backend. If this is consistently nonzero, your pool is too small or your transactions are too slow.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;avg_wait_time&lt;/code&gt; in &lt;code&gt;SHOW STATS&lt;/code&gt;: average microseconds clients spent waiting for a server connection. Should be near zero. Tens of milliseconds means contention.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sv_active&lt;/code&gt; vs &lt;code&gt;sv_idle&lt;/code&gt;: server connections currently doing work versus sitting in the pool. If &lt;code&gt;sv_active&lt;/code&gt; is at the pool size and &lt;code&gt;cl_waiting&lt;/code&gt; is climbing, you have your answer.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;avg_xact_time&lt;/code&gt; and &lt;code&gt;avg_query_time&lt;/code&gt;: how long the average transaction and query take. If transactions are way longer than queries, somebody is leaving transactions open.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can scrape these into Prometheus with one of several PgBouncer exporters. The metric you most want a dashboard on is &lt;code&gt;cl_waiting&lt;/code&gt; over time. The metric you most want an alert on is sustained &lt;code&gt;cl_waiting &amp;gt; 0&lt;/code&gt; for more than a minute or two, which means your pool sizing is wrong or your queries are slow.&lt;/p&gt;
&lt;h3&gt;16. Operational levers added in the last two years&lt;/h3&gt;
&lt;p&gt;A few features added since 2024 are worth knowing about because they fill specific operational gaps that used to require ugly workarounds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;transaction_timeout&lt;/code&gt; (1.25.0).&lt;/strong&gt; Caps the duration of any transaction at the pooler level. Without this, a client that opens a transaction and forgets to commit holds a backend forever, and your only recourse is killing the backend manually or waiting for &lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt; on the Postgres side. Set this to something reasonable (60 seconds is conservative for OLTP, longer for batch jobs that need their own pool) and runaway transactions clear themselves.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;KILL_CLIENT&lt;/code&gt; (1.24.0).&lt;/strong&gt; Lets you kill a specific client connection from the admin console without bouncing the whole pool. Combined with the unique IDs in &lt;code&gt;SHOW CLIENTS&lt;/code&gt; output, you can surgically remove the one bad actor instead of the nuclear &lt;code&gt;KILL pool&lt;/code&gt;. The combination of &lt;code&gt;SHOW CLIENTS&lt;/code&gt; to find the offender and &lt;code&gt;KILL_CLIENT&lt;/code&gt; to evict them is the modern equivalent of &lt;code&gt;pg_terminate_backend()&lt;/code&gt; at the pooler tier.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;max_user_client_connections&lt;/code&gt; and &lt;code&gt;max_db_client_connections&lt;/code&gt; (1.24.0).&lt;/strong&gt; These cap how many clients can connect to PgBouncer for a given user or database, separately from the cap on backend connections. The intuition: if a misbehaving service tries to open ten thousand connections to your pooler, the pooler accepts all of them and starves everyone else. These limits give you per-tenant fairness without splitting into separate PgBouncer instances.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;query_wait_notify&lt;/code&gt; (1.25.0).&lt;/strong&gt; When a client has been queued for a server connection for more than 5 seconds, PgBouncer sends a NOTICE message (&quot;queued for X seconds&quot;). This is great when debugging &quot;why is my query slow&quot; reports, because the latency might be entirely waiting on the pool, not the database. The default threshold is configurable.&lt;/p&gt;
&lt;p&gt;None of these are exotic. They&apos;re the kind of features that an SRE spending time with PgBouncer eventually wants. If you&apos;re running an older 1.21 or 1.22 from a Linux distro package, an upgrade buys you these.&lt;/p&gt;
&lt;h3&gt;17. A real story: the Mattermost case&lt;/h3&gt;
&lt;p&gt;The Mattermost team published a postmortem in 2022 that&apos;s worth reading because it shows how pool-size choices interact with multi-database/multi-tenant deployments in non-obvious ways.&lt;/p&gt;
&lt;p&gt;Mattermost runs thousands of customer workspaces grouped 10 to 15 to a logical database in a shared Postgres cluster, with each workspace getting its own PgBouncer pool. They put PgBouncer in front. Their initial config looked reasonable: &lt;code&gt;pool_mode = transaction&lt;/code&gt;, &lt;code&gt;min_pool_size = 20&lt;/code&gt;, &lt;code&gt;default_pool_size = 20&lt;/code&gt;, &lt;code&gt;reserve_pool_size = 5&lt;/code&gt;, &lt;code&gt;max_client_conn = 10000&lt;/code&gt;, &lt;code&gt;max_db_connections = 20&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Spot the problem? Each workspace gets its own pool of size 20. &lt;code&gt;max_db_connections = 20&lt;/code&gt; is the cap on real Postgres connections from &lt;em&gt;all&lt;/em&gt; workspaces combined. With 10 to 15 workspaces per database, every workspace was effectively competing for those 20 backend slots, and any time more than one workspace had a burst of activity, they were starving each other. The visible symptom was high CPU on the RDS cluster from connection churn: PgBouncer was constantly opening and closing real connections trying to satisfy workspaces that were each demanding more than the global cap allowed.&lt;/p&gt;
&lt;p&gt;The fix wasn&apos;t to raise the per-pool size proportionally. They dropped &lt;code&gt;min_pool_size&lt;/code&gt; from 20 to 1 and &lt;code&gt;default_pool_size&lt;/code&gt; from 20 to 5, so PgBouncer wasn&apos;t aggressively keeping warm connections that other pools needed. They raised &lt;code&gt;reserve_pool_size&lt;/code&gt; to 20 and shortened &lt;code&gt;reserve_pool_timeout&lt;/code&gt; from 5 seconds to 1, so bursty workspaces could grab reserve connections quickly. And critically, they raised &lt;code&gt;max_db_connections&lt;/code&gt; from 20 to 100, so the global cap stopped being the bottleneck. The lesson is that in multi-tenant setups, pool sizes are not independent. They share a global cap, and configurations that look fine in isolation can deadlock or thrash when multiplied across tenants.&lt;/p&gt;
&lt;p&gt;There was a secondary lesson too. They discovered that some of their migrations used &lt;code&gt;pg_advisory_lock&lt;/code&gt; at the session level, which doesn&apos;t work in transaction pooling. They had to either route migrations to a direct connection or rewrite them to use transaction-scoped locks. This is the kind of thing that doesn&apos;t show up in load testing and only surfaces when you actually run the migration against the pooled connection, so it&apos;s worth auditing your migration tooling explicitly when you adopt PgBouncer.&lt;/p&gt;
&lt;h3&gt;18. When PgBouncer is the wrong tool&lt;/h3&gt;
&lt;p&gt;It&apos;s worth saying out loud: PgBouncer doesn&apos;t fix every problem.&lt;/p&gt;
&lt;p&gt;If your app is genuinely doing heavy work on every connection, transaction pooling won&apos;t help much. Multiplexing only saves you if the connections are mostly idle. A worker process that&apos;s running queries 100% of the time will hold a backend the whole time, and PgBouncer adds latency without giving you ratio gains.&lt;/p&gt;
&lt;p&gt;If you need session-level features that don&apos;t have transaction-level analogs, transaction pooling will fight you. &lt;code&gt;LISTEN/NOTIFY&lt;/code&gt;-heavy designs, long-running cursors, session-locked migrations: stay on session pooling, or use direct connections for those code paths and pool the rest.&lt;/p&gt;
&lt;p&gt;If you need read/write splitting, query routing, automatic failover, or replication management, PgBouncer doesn&apos;t do those. Pgpool-II does, at the cost of being much more complex (multi-process, parses SQL, has its own opinions about your queries). Recent Rust-based alternatives like PgCat are multi-threaded and add some routing features. Cloud-managed proxies like AWS RDS Proxy or Azure&apos;s built-in PgBouncer take the operational burden off you. Each of these is a real tradeoff. PgBouncer&apos;s appeal is that it does one thing and does it cleanly. The minute you want it to do more, you&apos;re better off picking a tool that&apos;s meant for the more.&lt;/p&gt;
&lt;p&gt;And if the real problem is that your queries are slow, PgBouncer can&apos;t help you. A 2-second query holding a backend for 2 seconds is a backend that no other client can use, regardless of how many clients are waiting in PgBouncer&apos;s queue. Connection pooling is a multiplier on the time backends spend not running queries. If they&apos;re always running queries, the multiplier is 1.&lt;/p&gt;
&lt;h3&gt;19. Three ideas worth stealing&lt;/h3&gt;
&lt;p&gt;If you only take three things away from all this, I&apos;d pick these.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pool what&apos;s expensive, not what&apos;s logical.&lt;/strong&gt; Postgres connections are expensive because they map 1:1 onto OS processes. App-side connections are cheap because they&apos;re just file descriptors. Resist the instinct to make your app&apos;s connection count match your real backend count. They aren&apos;t the same kind of thing. The same logic shows up everywhere: HTTP keepalive, TCP connection reuse, request-scoped sessions in web frameworks. Whenever you have a cheap thing the application thinks of as &quot;the same as&quot; an expensive thing, there&apos;s a multiplexer in your future.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lying transparently is a strategy, until it isn&apos;t.&lt;/strong&gt; Transaction pooling works because it lies about what a session is, and the lie holds for most apps because most apps don&apos;t actually use sessions for anything. The interesting question isn&apos;t &quot;should I pool&quot; but &quot;which session features does my app accidentally rely on, and can I take them out?&quot; If you can, pool aggressively. If you can&apos;t, the lying breaks down and you should know which lies you&apos;re tolerating. The real engineering work is the audit, not the config.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The expensive case is the one you&apos;ll meet in production.&lt;/strong&gt; All the gotchas in this piece (statement_timeout leakage, search_path drift, advisory lock evaporation) only matter in the rare case. In the common case, transaction pooling is clean and obvious and your app works. The reason this stuff is worth knowing is because the rare cases are the ones that cause incidents, and incidents are the only times you&apos;ll be debugging connection pooling at all. The cost of learning this in a calm Tuesday afternoon is a lot lower than the cost of learning it during an outage.&lt;/p&gt;
&lt;h2&gt;Epilogue&lt;/h2&gt;
&lt;p&gt;The history of databases is full of these layers that don&apos;t quite fit. Postgres is great at being a transactional database. It is not great at handling a thousand short-lived connections, because that workload didn&apos;t exist when its connection model was designed. Putting PgBouncer in front of it is a fix for an impedance mismatch between two eras of computing: the one where a database was something a few analysts logged into directly, and the one where it&apos;s something serving billions of HTTP requests through dozens of stateless web workers.&lt;/p&gt;
&lt;p&gt;What&apos;s interesting is that the fix is not a Postgres feature. It&apos;s a separate program, written by different people, that intercepts the wire protocol and shuffles bytes. It works because Postgres&apos; protocol is open and stable, and because the place where the impedance mismatch lives (the cost of a backend process) is upstream of anything the app cares about. PgBouncer sits at the seam.&lt;/p&gt;
&lt;p&gt;You can read the development of features like protocol-level prepared statements in 1.21 as a slow tightening of that seam. The original PgBouncer was content to break a lot of session features outright, on the assumption that the multiplexing was worth it. Successive versions have figured out, one by one, how to support more session-flavored things without giving up the multiplexing. The pattern repeats: find a feature that breaks, look for a way to track its state across pooled backends, ship the fix. Prepared statements were the big one. &lt;code&gt;track_extra_parameters&lt;/code&gt; handles GUC propagation. LDAP support landed in 1.25 (November 2025). The seam stays a seam, but it gets less visible every release.&lt;/p&gt;
&lt;p&gt;There&apos;s no big lesson at the end of this. PgBouncer is a small program that does a small thing well. Most of the value of writing about it is just demystifying it, so that the next time someone runs into one of its sharp edges, they know it&apos;s a sharp edge and not magic. Postgres&apos; architecture is what it is. The pooler is a workaround. The workaround is solid, but it&apos;s a workaround, and knowing that is most of what you need to use it well.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The connection isn&apos;t the thing. The transaction is the thing. Everything else is bookkeeping.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;References and further reading&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;PgBouncer 1.25.1 release and full changelog. &lt;a href=&quot;https://www.pgbouncer.org/changelog.html&quot;&gt;pgbouncer.org/changelog&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;PgBouncer features and pooling-mode compatibility table. &lt;a href=&quot;https://www.pgbouncer.org/features.html&quot;&gt;pgbouncer.org/features&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;PgBouncer configuration reference, including &lt;code&gt;auth_query&lt;/code&gt;, &lt;code&gt;max_prepared_statements&lt;/code&gt;, and the full pgbouncer.ini schema. &lt;a href=&quot;https://www.pgbouncer.org/config.html&quot;&gt;pgbouncer.org/config&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Greg Sabino Mullane, &quot;Prepared Statements in Transaction Mode for PgBouncer,&quot; Crunchy Data, 2023. The canonical writeup of how 1.21&apos;s prepared-statement support actually works.&lt;/li&gt;
&lt;li&gt;JP Camara, &quot;PgBouncer is useful, important, and fraught with peril,&quot; 2023. The best long-form writeup I&apos;ve read on transaction-mode gotchas, with worked examples for statement_timeout leakage, advisory locks, and PHP/PDO quirks.&lt;/li&gt;
&lt;li&gt;Andres Freund, &quot;Measuring the Memory Overhead of a Postgres Connection,&quot; 2020. The careful per-connection memory measurement that &quot;10 MB&quot; comes from, with caveats.&lt;/li&gt;
&lt;li&gt;Crunchy Data, &quot;How to SCRAM in Postgres with pgBouncer.&quot; Walks through the SCRAM protocol mechanics that let PgBouncer authenticate to Postgres without ever seeing the plaintext password.&lt;/li&gt;
&lt;li&gt;Mattermost engineering, &quot;Optimizing Database Connection Loads With PgBouncer and Testwick,&quot; 2022. The multi-tenant pool-sizing case study used in this piece.&lt;/li&gt;
&lt;li&gt;Cybertec, &quot;PgBouncer Authentication Made Easy&quot; (Laurenz Albe). Practical SCRAM and &lt;code&gt;auth_query&lt;/code&gt; setup with a working SECURITY DEFINER pattern.&lt;/li&gt;
&lt;li&gt;Heroku, &quot;Best Practices for PgBouncer Configuration.&quot; Production-flavored sizing guidance and discussion of preboot interaction.&lt;/li&gt;
&lt;li&gt;Microsoft Azure docs, &quot;PgBouncer in Azure Database for PostgreSQL.&quot; Documents the managed-deployment pattern that ships PgBouncer 1.25.1 on the same VM as the database.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>The Unified Vision Stack</title><link>https://www.augusteo.com/blog/unified-vision-stack/</link><guid isPermaLink="true">https://www.augusteo.com/blog/unified-vision-stack/</guid><description>An end-to-end, intuition-first explanation of how modern computer vision works, and why three papers between late 2024 and early 2026 quietly changed the standard stack.</description><pubDate>Fri, 24 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;An end-to-end, intuition-first explanation of how modern computer vision works, and why three papers published between late 2024 and early 2026 quietly changed the standard stack. For engineers new to the field. About a 45-minute read.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;1. What a vision model actually does&lt;/h3&gt;
&lt;p&gt;Before anything else, we have to agree on what a computer vision model is supposed to produce. If you&apos;ve used ML libraries you&apos;ve probably called a function like &lt;code&gt;model.predict(image)&lt;/code&gt; and gotten back a label. But the label is not really what the model makes. The label is what a tiny head stapled onto the model makes, using what the model actually produces: a pile of numbers.&lt;/p&gt;
&lt;p&gt;That pile of numbers is called an &lt;em&gt;embedding&lt;/em&gt;, or a &lt;em&gt;feature&lt;/em&gt;, or a &lt;em&gt;representation&lt;/em&gt; (the terms are used interchangeably). An image comes in. A vector comes out. The vector is a compact description of what&apos;s in the image, expressed in a language the model invented for itself during training.&lt;/p&gt;
&lt;p&gt;Everything downstream of a vision model is a function from this vector to something useful. Classification is a small function that maps the vector to a list of probabilities. Semantic segmentation is a larger function that maps it to a per-pixel label. Image retrieval uses the vector directly. Find other vectors close to this one, and you&apos;ve found similar images. Depth estimation, object detection, video tracking, visual question answering: all of them are small heads attached to a big backbone. The backbone is where the work lives.&lt;/p&gt;
&lt;p&gt;So when we talk about vision foundation models, we&apos;re talking about one specific thing: the function that turns a raw image into a vector. The question that the entire field is really asking is: &lt;em&gt;what makes one vector better than another?&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A useful analogy for engineers: the vision backbone is like a compiler&apos;s intermediate representation. The IR is not what you care about directly, but every single optimization pass and every single backend target uses it. Get the IR right and every downstream pass gets easier. Get it wrong and you pay for it forever.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;2. Two kinds of features, and why it matters&lt;/h3&gt;
&lt;p&gt;Here&apos;s where we need to make a distinction that will shape the rest of this piece. A vision model produces two different kinds of features, and they have very different jobs.&lt;/p&gt;
&lt;p&gt;The first is a &lt;em&gt;global feature&lt;/em&gt;. It&apos;s a single vector that describes the whole image. If the image is a golden retriever running on a beach, the global feature is something like &quot;a vector that means beach-with-dog.&quot; You can compare it to other global features to find similar images. You can run a linear classifier on it to ask &quot;is there a dog in this picture?&quot; It is a summary.&lt;/p&gt;
&lt;p&gt;The second is a set of &lt;em&gt;dense features&lt;/em&gt;: one vector per region of the image. If you imagine the image cut into a grid of small patches, each patch gets its own vector. The dense features are a map. Patches of fur get one kind of vector, patches of sky get another, patches of sand get a third. The spatial arrangement is preserved: the vector in the top-right of the feature map corresponds to what was in the top-right of the image.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 260&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; height=&quot;auto&quot;&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;40&quot; width=&quot;180&quot; height=&quot;180&quot; fill=&quot;#EDE5D4&quot; stroke=&quot;#8F8578&quot;/&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;40&quot; width=&quot;180&quot; height=&quot;70&quot; fill=&quot;#93C5FD&quot; opacity=&quot;.7&quot;/&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;110&quot; width=&quot;180&quot; height=&quot;50&quot; fill=&quot;#D1AE7A&quot; opacity=&quot;.8&quot;/&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;160&quot; width=&quot;180&quot; height=&quot;60&quot; fill=&quot;#059669&quot; opacity=&quot;.6&quot;/&amp;gt;
&amp;lt;circle cx=&quot;155&quot; cy=&quot;75&quot; r=&quot;12&quot; fill=&quot;#FEF3C7&quot;/&amp;gt;
&amp;lt;text x=&quot;120&quot; y=&quot;240&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#6B6258&quot;&amp;gt;input image&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;225&quot; y1=&quot;130&quot; x2=&quot;265&quot; y2=&quot;130&quot; stroke=&quot;#6B6258&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#a1)&quot;/&amp;gt;
&amp;lt;defs&amp;gt;&amp;lt;marker id=&quot;a1&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;6&quot; markerHeight=&quot;6&quot; orient=&quot;auto&quot;&amp;gt;&amp;lt;path d=&quot;M0,0 L10,5 L0,10 z&quot; fill=&quot;#6B6258&quot;/&amp;gt;&amp;lt;/marker&amp;gt;&amp;lt;/defs&amp;gt;
&amp;lt;text x=&quot;395&quot; y=&quot;55&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot; letter-spacing=&quot;1.5&quot;&amp;gt;GLOBAL FEATURE&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;280&quot; y=&quot;65&quot; width=&quot;260&quot; height=&quot;28&quot; fill=&quot;none&quot; stroke=&quot;#2563EB&quot;/&amp;gt;
&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#2563EB&quot;&amp;gt;&amp;lt;text x=&quot;290&quot; y=&quot;84&quot;&amp;gt;0.7&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;315&quot; y=&quot;84&quot;&amp;gt;-0.2&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;345&quot; y=&quot;84&quot;&amp;gt;0.1&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;368&quot; y=&quot;84&quot;&amp;gt;0.5&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;393&quot; y=&quot;84&quot;&amp;gt;0.0&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;418&quot; y=&quot;84&quot;&amp;gt;-0.4&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;450&quot; y=&quot;84&quot;&amp;gt;0.8&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;478&quot; y=&quot;84&quot;&amp;gt;...&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;505&quot; y=&quot;84&quot;&amp;gt;0.3&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;395&quot; y=&quot;115&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;12&quot; fill=&quot;#6B6258&quot; font-style=&quot;italic&quot;&amp;gt;one vector. says &quot;beach with dog.&quot;&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;395&quot; y=&quot;150&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot; letter-spacing=&quot;1.5&quot;&amp;gt;DENSE FEATURES&amp;lt;/text&amp;gt;
&amp;lt;g transform=&quot;translate(280,160)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;38&quot; height=&quot;20&quot; fill=&quot;#93C5FD&quot;/&amp;gt;&amp;lt;rect x=&quot;40&quot; y=&quot;0&quot; width=&quot;38&quot; height=&quot;20&quot; fill=&quot;#93C5FD&quot;/&amp;gt;&amp;lt;rect x=&quot;80&quot; y=&quot;0&quot; width=&quot;38&quot; height=&quot;20&quot; fill=&quot;#93C5FD&quot;/&amp;gt;&amp;lt;rect x=&quot;120&quot; y=&quot;0&quot; width=&quot;38&quot; height=&quot;20&quot; fill=&quot;#FEF3C7&quot;/&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;22&quot; width=&quot;38&quot; height=&quot;20&quot; fill=&quot;#D1AE7A&quot;/&amp;gt;&amp;lt;rect x=&quot;40&quot; y=&quot;22&quot; width=&quot;38&quot; height=&quot;20&quot; fill=&quot;#D1AE7A&quot;/&amp;gt;&amp;lt;rect x=&quot;80&quot; y=&quot;22&quot; width=&quot;38&quot; height=&quot;20&quot; fill=&quot;#92400E&quot;/&amp;gt;&amp;lt;rect x=&quot;120&quot; y=&quot;22&quot; width=&quot;38&quot; height=&quot;20&quot; fill=&quot;#D1AE7A&quot;/&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;44&quot; width=&quot;38&quot; height=&quot;20&quot; fill=&quot;#059669&quot;/&amp;gt;&amp;lt;rect x=&quot;40&quot; y=&quot;44&quot; width=&quot;38&quot; height=&quot;20&quot; fill=&quot;#059669&quot;/&amp;gt;&amp;lt;rect x=&quot;80&quot; y=&quot;44&quot; width=&quot;38&quot; height=&quot;20&quot; fill=&quot;#059669&quot;/&amp;gt;&amp;lt;rect x=&quot;120&quot; y=&quot;44&quot; width=&quot;38&quot; height=&quot;20&quot; fill=&quot;#059669&quot;/&amp;gt;
&amp;lt;g fill=&quot;none&quot; stroke=&quot;#F4EEE3&quot;&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;38&quot; height=&quot;20&quot;/&amp;gt;&amp;lt;rect x=&quot;40&quot; y=&quot;0&quot; width=&quot;38&quot; height=&quot;20&quot;/&amp;gt;&amp;lt;rect x=&quot;80&quot; y=&quot;0&quot; width=&quot;38&quot; height=&quot;20&quot;/&amp;gt;&amp;lt;rect x=&quot;120&quot; y=&quot;0&quot; width=&quot;38&quot; height=&quot;20&quot;/&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;22&quot; width=&quot;38&quot; height=&quot;20&quot;/&amp;gt;&amp;lt;rect x=&quot;40&quot; y=&quot;22&quot; width=&quot;38&quot; height=&quot;20&quot;/&amp;gt;&amp;lt;rect x=&quot;80&quot; y=&quot;22&quot; width=&quot;38&quot; height=&quot;20&quot;/&amp;gt;&amp;lt;rect x=&quot;120&quot; y=&quot;22&quot; width=&quot;38&quot; height=&quot;20&quot;/&amp;gt;&amp;lt;rect x=&quot;0&quot; y=&quot;44&quot; width=&quot;38&quot; height=&quot;20&quot;/&amp;gt;&amp;lt;rect x=&quot;40&quot; y=&quot;44&quot; width=&quot;38&quot; height=&quot;20&quot;/&amp;gt;&amp;lt;rect x=&quot;80&quot; y=&quot;44&quot; width=&quot;38&quot; height=&quot;20&quot;/&amp;gt;&amp;lt;rect x=&quot;120&quot; y=&quot;44&quot; width=&quot;38&quot; height=&quot;20&quot;/&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;395&quot; y=&quot;245&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;12&quot; fill=&quot;#6B6258&quot; font-style=&quot;italic&quot;&amp;gt;a grid of vectors. says &quot;sky here, dog there, sand below.&quot;&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 1.&amp;lt;/strong&amp;gt; The same image produces two kinds of output. A single global vector is enough to classify the whole image. A grid of dense vectors is required to tell one region from another.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;Here&apos;s why the distinction matters enormously. For a long time, the dominant vision model in ML was OpenAI&apos;s CLIP, trained on hundreds of millions of image-caption pairs, designed to produce one clean global feature per image. CLIP is great at classification, retrieval, and zero-shot labeling. For dense tasks (segmentation, depth, anything pixel-precise) CLIP features are mush. They were never optimized for spatial detail, so they don&apos;t have it.&lt;/p&gt;
&lt;p&gt;If you want a single vision backbone that can do everything (classify, segment, track, measure depth, correspond points between frames), you need both kinds of features to be good. Global features that summarize well. Dense features that preserve local geometry. Seems like an obvious thing to want. Turns out, until very recently, nobody knew how to train a single network to produce both at scale.&lt;/p&gt;
&lt;h3&gt;3. How a Vision Transformer sees&lt;/h3&gt;
&lt;p&gt;Modern vision is dominated by a single architecture family: the &lt;em&gt;Vision Transformer&lt;/em&gt;, or ViT. It&apos;s worth spending a paragraph on how one works, because the rest of the story depends on it.&lt;/p&gt;
&lt;p&gt;A ViT takes an image (say a 224×224 RGB picture) and chops it into a grid of non-overlapping patches, typically 16×16 pixels each. That&apos;s 14×14 = 196 patches. Each patch is flattened into a vector of pixel values and linearly projected to a fixed-width embedding, say 1024 dimensions. A special &quot;classification token&quot; (an extra learned vector) is prepended to the sequence. So the input to the transformer is a sequence of 197 vectors.&lt;/p&gt;
&lt;p&gt;Then: standard transformer layers. Multi-head self-attention followed by feed-forward networks, repeated for dozens of layers. The attention mechanism lets each patch look at every other patch, so by the time we reach the top of the stack, every patch&apos;s vector has been shaped by information from every other patch.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# What a ViT forward pass looks like, roughly
def vit(image):
    patches = chop_into_16x16(image)           # 196 patches of 256 pixels
    tokens  = linear_project(patches)          # 196 vectors of dim 1024
    tokens  = prepend(CLS_TOKEN, tokens)       # now 197 vectors
    tokens  = add_positional_encoding(tokens)
    for layer in transformer_layers:
        tokens = layer(tokens)                 # each token attends to all others
    global_feature = tokens[0]                 # the CLS token
    dense_features = tokens[1:]                # the 196 patch tokens
    return global_feature, dense_features
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the end of the stack, the vector at position 0 (the CLS token) is your global feature. The vectors at positions 1 through 196 are your dense features, each one representing what&apos;s at that patch&apos;s location in the image. The attention mechanism has already mixed information between patches, but each final token is still positionally anchored to its input patch.&lt;/p&gt;
&lt;p&gt;This is nice, clean, general. A single architecture that naturally produces both global and dense features. You can scale it by making the tokens wider, the layers deeper, or the patches smaller. The scaling behavior is smooth and well-understood from the NLP side.&lt;/p&gt;
&lt;p&gt;And yet.&lt;/p&gt;
&lt;h3&gt;4. The paradox: scaling made it &lt;em&gt;worse&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;If you take a ViT and train it on enough data for long enough, something strange happens. The global features keep getting better. Classification accuracy on ImageNet climbs monotonically, breaking past 85%, past 88%, past 90%. The model is clearly learning more and more about images.&lt;/p&gt;
&lt;p&gt;The dense features, meanwhile, peak early and then &lt;em&gt;get worse&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This isn&apos;t a subtle effect. Researchers have measured it carefully: on standard dense-feature benchmarks like ADE20k semantic segmentation and Pascal VOC, the quality of a large ViT&apos;s dense features rises during the first few hundred thousand training iterations, peaks, and then degrades steadily until the features become nearly unusable for pixel-level tasks. The phenomenon is called &lt;em&gt;dense feature collapse&lt;/em&gt;, and it only really shows up at scale. Smaller models (around ViT-Large, roughly 300M params and below) don&apos;t suffer from it much, because they don&apos;t have the capacity to aggressively specialize toward any one objective and just produce mediocre-but-balanced representations. Past that scale, the model has the capacity to aggressively optimize the loudest signal in the loss function, which, for self-supervised training, is the global clustering objective. The fine-grained per-patch distinctions that dense tasks depend on get sacrificed to make the global summary cleaner.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 320&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; height=&quot;auto&quot;&amp;gt;
&amp;lt;line x1=&quot;60&quot; y1=&quot;260&quot; x2=&quot;620&quot; y2=&quot;260&quot; stroke=&quot;#8F8578&quot;/&amp;gt;
&amp;lt;line x1=&quot;60&quot; y1=&quot;260&quot; x2=&quot;60&quot; y2=&quot;40&quot; stroke=&quot;#8F8578&quot;/&amp;gt;
&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot; text-anchor=&quot;middle&quot;&amp;gt;
&amp;lt;text x=&quot;60&quot; y=&quot;278&quot;&amp;gt;0&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;172&quot; y=&quot;278&quot;&amp;gt;200k&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;284&quot; y=&quot;278&quot;&amp;gt;400k&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;396&quot; y=&quot;278&quot;&amp;gt;600k&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;508&quot; y=&quot;278&quot;&amp;gt;800k&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;620&quot; y=&quot;278&quot;&amp;gt;1M&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;340&quot; y=&quot;300&quot; font-size=&quot;11&quot;&amp;gt;training iterations&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g stroke=&quot;#C9BEAA&quot; stroke-width=&quot;.5&quot; stroke-dasharray=&quot;2,3&quot;&amp;gt;&amp;lt;line x1=&quot;60&quot; y1=&quot;100&quot; x2=&quot;620&quot; y2=&quot;100&quot;/&amp;gt;&amp;lt;line x1=&quot;60&quot; y1=&quot;160&quot; x2=&quot;620&quot; y2=&quot;160&quot;/&amp;gt;&amp;lt;line x1=&quot;60&quot; y1=&quot;220&quot; x2=&quot;620&quot; y2=&quot;220&quot;/&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;path d=&quot;M60,240 Q120,160 172,105 T284,75 T396,62 T508,58 T620,56&quot; fill=&quot;none&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;2.5&quot;/&amp;gt;
&amp;lt;path d=&quot;M60,240 Q100,150 160,120 Q220,105 260,125 Q320,180 390,220 Q460,240 540,250 Q580,252 620,252&quot; fill=&quot;none&quot; stroke=&quot;#B91C1C&quot; stroke-width=&quot;2.5&quot;/&amp;gt;
&amp;lt;g font-family=&quot;serif&quot; font-size=&quot;12&quot; font-style=&quot;italic&quot;&amp;gt;
&amp;lt;text x=&quot;425&quot; y=&quot;28&quot; fill=&quot;#2563EB&quot;&amp;gt;global accuracy (ImageNet)&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;450&quot; y1=&quot;34&quot; x2=&quot;478&quot; y2=&quot;54&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;.8&quot;/&amp;gt;
&amp;lt;text x=&quot;332&quot; y=&quot;175&quot; fill=&quot;#B91C1C&quot;&amp;gt;dense precision (ADE20k mIoU)&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;352&quot; y1=&quot;182&quot; x2=&quot;372&quot; y2=&quot;207&quot; stroke=&quot;#B91C1C&quot; stroke-width=&quot;.8&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g font-family=&quot;serif&quot; font-size=&quot;12&quot; fill=&quot;#6B6258&quot;&amp;gt;
&amp;lt;text x=&quot;150&quot; y=&quot;85&quot; text-anchor=&quot;middle&quot;&amp;gt;rises and keeps rising&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;500&quot; y=&quot;200&quot; text-anchor=&quot;middle&quot;&amp;gt;rises, peaks, collapses&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 2.&amp;lt;/strong&amp;gt; The two curves that define the problem. Global classification accuracy rises steadily through a million training iterations. Dense feature quality peaks around iteration 200,000 and then degrades for the rest of training. This is the failure mode every modern paper is ultimately trying to repair.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;Why does this happen? The intuition, once you see it, is hard to unsee.&lt;/p&gt;
&lt;p&gt;The self-supervised training objective is fundamentally a &lt;em&gt;clustering&lt;/em&gt; objective. It asks: &quot;given two views of the same image, make their global summaries land in the same place in embedding space.&quot; In other words, it rewards the model for saying that two patches (a patch of fur from view A and a patch of fur from view B) describe the same thing.&lt;/p&gt;
&lt;p&gt;But &quot;describe the same thing&quot; is a slippery requirement. The easiest way to satisfy it is to make all patches of anything-dog-related look similar in feature space. Fur patches, eye patches, ear patches, paw patches: push them all toward a shared &quot;dog&quot; region of the feature space. That&apos;s great for classification, because now a simple average of the patch features is a clean signal for the dog concept. But it&apos;s disastrous for segmentation, because now the model has no way to tell a fur pixel from an eye pixel. They&apos;re in the same neighborhood of feature space.&lt;/p&gt;
&lt;p&gt;The longer you train, the more the global objective wins. Patches get pulled toward semantic centers. Local distinctions dissolve. The model becomes a better classifier and a worse mapmaker, and there&apos;s no obvious way to stop the dissolution without also stopping the improvement.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The same loss that teaches the model to see teaches it to stop looking.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is the paradox that every paper we&apos;re going to discuss is ultimately responding to. The story of modern vision foundation models is the story of a series of increasingly clever ways to preserve local detail while still letting global understanding improve. Three in particular (DINOv3, C-RADIOv4, and FeatSharp) form a coherent stack that now dominates the state of the art.&lt;/p&gt;
&lt;p&gt;We&apos;ll take them one at a time.&lt;/p&gt;
&lt;h2&gt;Act 1 — The Lens&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;DINOv3, self-supervised learning at scale, and the trick that saved dense features.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;5. Meet DINOv3&lt;/h3&gt;
&lt;p&gt;DINOv3 is Meta&apos;s self-supervised vision transformer, released in August 2025. It is, at the time of writing, the strongest general-purpose vision backbone in the world. It comes in sizes from 21 million parameters (deployable on a laptop) up to 6.7 billion parameters (the research flagship). It was trained on 1.7 billion images without a single human-provided label.&lt;/p&gt;
&lt;p&gt;Three things make DINOv3 interesting. First, it took self-supervised learning to a scale nobody thought it could reach: 12× more data and roughly 6× more parameters than its predecessor DINOv2. Second, it produced dense features so good that a frozen DINOv3 backbone outperforms specialized networks on tasks like satellite canopy-height estimation and medical pathology, without any fine-tuning. Third, it solved dense feature collapse (the paradox we just described) with a clean technique called &lt;em&gt;Gram anchoring&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Gram anchoring is the real contribution. Everything else (the scale, the data curation, the architecture tweaks) is table stakes for a 2025 foundation model. Gram anchoring is the idea that will still be influential in five years. Before we can appreciate it, though, we need to understand two pieces of machinery: self-supervised learning, and the Gram matrix itself.&lt;/p&gt;
&lt;h3&gt;6. Self-supervision, from first principles&lt;/h3&gt;
&lt;p&gt;If you&apos;re coming from supervised learning, the obvious question is: how do you train a model without labels?&lt;/p&gt;
&lt;p&gt;The trick is to invent your own labels by exploiting the structure of the data. In images, a lot of structure is available for free. Two crops of the same image should &quot;mean&quot; the same thing. A masked-out patch of an image should be predictable from the patches around it. A rotated image is still the same image. These are all free sources of supervision. They require no human annotation, just a clever way of asking the model a question whose answer can be checked automatically.&lt;/p&gt;
&lt;p&gt;The DINO family (DINO, DINOv2, and now DINOv3) uses a specific technique called &lt;em&gt;self-distillation&lt;/em&gt;. Here&apos;s how it works.&lt;/p&gt;
&lt;p&gt;You have two copies of the network: a &lt;em&gt;student&lt;/em&gt; (which you actively train) and a &lt;em&gt;teacher&lt;/em&gt; (which is updated as a slow-moving average of the student&apos;s weights, an exponential moving average, or EMA). Both networks are the same architecture. The teacher is always a slightly stale version of the student.&lt;/p&gt;
&lt;p&gt;On each training step, you take a single image and produce two different &quot;views&quot; of it: two random crops, different augmentations, different scales. The student sees one view. The teacher sees the other. Both produce a feature vector. You compute a loss that says: &quot;student, your feature should look like the teacher&apos;s feature.&quot; Then you backprop through the student, and the teacher updates by sliding toward the student&apos;s new weights.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 240&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; height=&quot;auto&quot;&amp;gt;
&amp;lt;rect x=&quot;20&quot; y=&quot;90&quot; width=&quot;80&quot; height=&quot;80&quot; fill=&quot;#EDE5D4&quot; stroke=&quot;#8F8578&quot;/&amp;gt;
&amp;lt;rect x=&quot;20&quot; y=&quot;90&quot; width=&quot;80&quot; height=&quot;30&quot; fill=&quot;#93C5FD&quot; opacity=&quot;.7&quot;/&amp;gt;
&amp;lt;rect x=&quot;20&quot; y=&quot;130&quot; width=&quot;80&quot; height=&quot;20&quot; fill=&quot;#D1AE7A&quot; opacity=&quot;.8&quot;/&amp;gt;
&amp;lt;rect x=&quot;20&quot; y=&quot;150&quot; width=&quot;80&quot; height=&quot;20&quot; fill=&quot;#059669&quot; opacity=&quot;.6&quot;/&amp;gt;
&amp;lt;text x=&quot;60&quot; y=&quot;190&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot;&amp;gt;source&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;140&quot; y=&quot;50&quot; width=&quot;65&quot; height=&quot;65&quot; fill=&quot;#EDE5D4&quot; stroke=&quot;#8F8578&quot;/&amp;gt;
&amp;lt;rect x=&quot;140&quot; y=&quot;50&quot; width=&quot;65&quot; height=&quot;25&quot; fill=&quot;#93C5FD&quot; opacity=&quot;.7&quot;/&amp;gt;
&amp;lt;rect x=&quot;140&quot; y=&quot;75&quot; width=&quot;65&quot; height=&quot;20&quot; fill=&quot;#D1AE7A&quot; opacity=&quot;.8&quot;/&amp;gt;
&amp;lt;rect x=&quot;140&quot; y=&quot;95&quot; width=&quot;65&quot; height=&quot;20&quot; fill=&quot;#059669&quot; opacity=&quot;.6&quot;/&amp;gt;
&amp;lt;text x=&quot;172&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;view A&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;140&quot; y=&quot;150&quot; width=&quot;65&quot; height=&quot;65&quot; fill=&quot;#EDE5D4&quot; stroke=&quot;#8F8578&quot;/&amp;gt;
&amp;lt;rect x=&quot;140&quot; y=&quot;150&quot; width=&quot;65&quot; height=&quot;20&quot; fill=&quot;#93C5FD&quot; opacity=&quot;.7&quot;/&amp;gt;
&amp;lt;rect x=&quot;140&quot; y=&quot;170&quot; width=&quot;65&quot; height=&quot;25&quot; fill=&quot;#D1AE7A&quot; opacity=&quot;.8&quot;/&amp;gt;
&amp;lt;rect x=&quot;140&quot; y=&quot;195&quot; width=&quot;65&quot; height=&quot;20&quot; fill=&quot;#059669&quot; opacity=&quot;.6&quot;/&amp;gt;
&amp;lt;text x=&quot;172&quot; y=&quot;232&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;view B&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;102&quot; y1=&quot;115&quot; x2=&quot;138&quot; y2=&quot;82&quot; stroke=&quot;#8F8578&quot;/&amp;gt;
&amp;lt;line x1=&quot;102&quot; y1=&quot;145&quot; x2=&quot;138&quot; y2=&quot;182&quot; stroke=&quot;#8F8578&quot;/&amp;gt;
&amp;lt;rect x=&quot;240&quot; y=&quot;45&quot; width=&quot;100&quot; height=&quot;75&quot; fill=&quot;none&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;290&quot; y=&quot;75&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;14&quot; fill=&quot;#2563EB&quot; font-weight=&quot;600&quot;&amp;gt;student&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;290&quot; y=&quot;95&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;being trained&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;240&quot; y=&quot;145&quot; width=&quot;100&quot; height=&quot;75&quot; fill=&quot;none&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;290&quot; y=&quot;175&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;14&quot; fill=&quot;#B8651A&quot; font-weight=&quot;600&quot;&amp;gt;teacher&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;290&quot; y=&quot;195&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;EMA of student&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;205&quot; y1=&quot;82&quot; x2=&quot;238&quot; y2=&quot;82&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a2)&quot;/&amp;gt;
&amp;lt;line x1=&quot;205&quot; y1=&quot;182&quot; x2=&quot;238&quot; y2=&quot;182&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a2)&quot;/&amp;gt;
&amp;lt;defs&amp;gt;&amp;lt;marker id=&quot;a2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;5&quot; markerHeight=&quot;5&quot; orient=&quot;auto&quot;&amp;gt;&amp;lt;path d=&quot;M0,0 L10,5 L0,10 z&quot; fill=&quot;#6B6258&quot;/&amp;gt;&amp;lt;/marker&amp;gt;&amp;lt;/defs&amp;gt;
&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot;&amp;gt;
&amp;lt;rect x=&quot;370&quot; y=&quot;70&quot; width=&quot;120&quot; height=&quot;22&quot; fill=&quot;none&quot; stroke=&quot;#2563EB&quot;/&amp;gt;
&amp;lt;text x=&quot;430&quot; y=&quot;86&quot; text-anchor=&quot;middle&quot; fill=&quot;#2563EB&quot;&amp;gt;[0.7, -0.1, 0.4, ...]&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;370&quot; y=&quot;170&quot; width=&quot;120&quot; height=&quot;22&quot; fill=&quot;none&quot; stroke=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;text x=&quot;430&quot; y=&quot;186&quot; text-anchor=&quot;middle&quot; fill=&quot;#B8651A&quot;&amp;gt;[0.8, -0.2, 0.3, ...]&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;line x1=&quot;342&quot; y1=&quot;82&quot; x2=&quot;368&quot; y2=&quot;82&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a2)&quot;/&amp;gt;
&amp;lt;line x1=&quot;342&quot; y1=&quot;182&quot; x2=&quot;368&quot; y2=&quot;182&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a2)&quot;/&amp;gt;
&amp;lt;line x1=&quot;430&quot; y1=&quot;92&quot; x2=&quot;430&quot; y2=&quot;125&quot; stroke=&quot;#6B6258&quot; stroke-dasharray=&quot;3,3&quot;/&amp;gt;
&amp;lt;line x1=&quot;430&quot; y1=&quot;168&quot; x2=&quot;430&quot; y2=&quot;135&quot; stroke=&quot;#6B6258&quot; stroke-dasharray=&quot;3,3&quot;/&amp;gt;
&amp;lt;rect x=&quot;520&quot; y=&quot;115&quot; width=&quot;120&quot; height=&quot;38&quot; fill=&quot;#1A1A1A&quot; stroke=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;text x=&quot;580&quot; y=&quot;132&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#B8651A&quot; letter-spacing=&quot;1&quot;&amp;gt;LOSS&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;580&quot; y=&quot;147&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#F4EEE3&quot;&amp;gt;‖ s − t ‖²&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;495&quot; y1=&quot;130&quot; x2=&quot;518&quot; y2=&quot;130&quot; stroke=&quot;#6B6258&quot; marker-end=&quot;url(#a2)&quot;/&amp;gt;
&amp;lt;line x1=&quot;430&quot; y1=&quot;130&quot; x2=&quot;495&quot; y2=&quot;130&quot; stroke=&quot;#6B6258&quot; stroke-dasharray=&quot;3,3&quot;/&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 3.&amp;lt;/strong&amp;gt; Self-distillation: the student and teacher see different views of the same image. The student is trained to match the teacher&apos;s output. The teacher is a slow exponential moving average of the student. Neither network ever sees a label, but the pair pressures each other into producing stable, consistent features across augmentations.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;This procedure has no obvious reason to work. There&apos;s no ground truth. The teacher is just another copy of the student, slightly behind in time. You could imagine the whole thing collapsing into a trivial solution: the student outputs a constant vector, the teacher outputs the same constant vector, everyone&apos;s happy and nothing has been learned.&lt;/p&gt;
&lt;p&gt;Preventing that collapse is a big part of what the DINO papers are about. The tricks include: careful output-space normalization (via Sinkhorn-Knopp soft clustering), an asymmetric crop scheme (the teacher only sees two large &quot;global&quot; crops of the image; the student sees those global crops plus several smaller &quot;local&quot; crops, and has to match the teacher&apos;s global view from every one of its own views, including the tiny zoomed-in ones), and a lot of temperature-scaling gymnastics. What matters for us is that it &lt;em&gt;does&lt;/em&gt; work, and when it works, it works spectacularly. The features DINO learns are so good that frozen DINO backbones (no task-specific fine-tuning) routinely beat specialized supervised networks on individual tasks.&lt;/p&gt;
&lt;p&gt;On top of the self-distillation loss (which enforces global consistency), DINOv3 adds a second loss called &lt;em&gt;iBOT&lt;/em&gt;, which is a masked patch prediction objective. Some percentage of the patches in the student&apos;s input are masked out (replaced with a special learnable &quot;mask&quot; token) and the student is trained to produce features for those masked patches that match what the teacher would have produced if it had seen them. This is the vision analog of BERT&apos;s masked-language-modeling objective, and it specifically targets &lt;em&gt;local&lt;/em&gt; quality: the student has to learn something about each patch that lets it stand in for the real content.&lt;/p&gt;
&lt;p&gt;Two losses. One global, one local. Trained together for a million iterations on 1.7 billion images. You might reasonably ask: if iBOT is explicitly a local-quality objective, shouldn&apos;t it prevent dense collapse? The short answer is that at multi-billion-parameter scale, the global DINO pressure (which is what the whole network is fundamentally oriented around) simply outweighs iBOT&apos;s local pressure. iBOT buys some protection early in training. It&apos;s not enough on its own. For the first couple hundred thousand iterations, everything is fine. Somewhere around iteration 200,000, something breaks.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;An aside on data.&lt;/strong&gt; The 1.7-billion-image dataset, called LVD-1689M, was not just scraped off the internet. It was filtered down from a starting pool of about 17 billion web images using an algorithm called hierarchical k-means clustering.&lt;/p&gt;
&lt;p&gt;The intuition: the internet is skewed. If you train a model on whatever images happen to be popular on social media, it sees millions of selfies and vacation photos but almost no industrial equipment, medical imagery, or aerial photography. The resulting model learns the skew. Hierarchical k-means groups images into categories, then sub-categories, recursively. Training draws samples &lt;em&gt;uniformly&lt;/em&gt; from the leaf clusters rather than from the raw distribution. Rare concepts get proportional attention. Common concepts don&apos;t drown them out.&lt;/p&gt;
&lt;p&gt;Think of it as stratified sampling on steroids. You get to see every corner of visual reality, not just the bright-lit center.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;7. A Gram matrix, from scratch&lt;/h3&gt;
&lt;p&gt;To understand DINOv3&apos;s fix for dense feature collapse, we need to introduce one more piece of math: the &lt;em&gt;Gram matrix&lt;/em&gt;. It sounds technical but the idea is almost absurdly simple.&lt;/p&gt;
&lt;p&gt;Start with a set of vectors. For our purposes, imagine the dense features coming out of a ViT: one vector per image patch. Say we have 196 patches, so 196 vectors, each maybe 1024 dimensions wide.&lt;/p&gt;
&lt;p&gt;The Gram matrix of those vectors is a square table where entry (i, j) is the dot product between vector i and vector j. If you have 196 vectors, the Gram matrix is 196 by 196. It doesn&apos;t care about the raw values of the vectors. It only records how they relate to each other.&lt;/p&gt;
&lt;p&gt;Let&apos;s do a concrete tiny example. Suppose we have only 4 patches, labeled A, B, C, and D. A and B are both patches of sky. C is a patch of fur. D is a patch of grass. The model produces a 3-dimensional feature vector for each patch:&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 310&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; height=&quot;auto&quot;&amp;gt;
&amp;lt;g transform=&quot;translate(30,40)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;70&quot; height=&quot;70&quot; fill=&quot;#93C5FD&quot; stroke=&quot;#F4EEE3&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;35&quot; y=&quot;42&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;28&quot; font-weight=&quot;700&quot; fill=&quot;#FFFFFF&quot;&amp;gt;A&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;72&quot; y=&quot;0&quot; width=&quot;70&quot; height=&quot;70&quot; fill=&quot;#93C5FD&quot; stroke=&quot;#F4EEE3&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;107&quot; y=&quot;42&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;28&quot; font-weight=&quot;700&quot; fill=&quot;#FFFFFF&quot;&amp;gt;B&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;72&quot; width=&quot;70&quot; height=&quot;70&quot; fill=&quot;#92400E&quot; stroke=&quot;#F4EEE3&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;35&quot; y=&quot;115&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;28&quot; font-weight=&quot;700&quot; fill=&quot;#FFFFFF&quot;&amp;gt;C&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;72&quot; y=&quot;72&quot; width=&quot;70&quot; height=&quot;70&quot; fill=&quot;#059669&quot; stroke=&quot;#F4EEE3&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;107&quot; y=&quot;115&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;28&quot; font-weight=&quot;700&quot; fill=&quot;#FFFFFF&quot;&amp;gt;D&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;142&quot; height=&quot;142&quot; fill=&quot;none&quot; stroke=&quot;#1A1A1A&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;71&quot; y=&quot;170&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;12&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;A, B = sky&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;71&quot; y=&quot;186&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;12&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;C = fur  ·  D = grass&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(230,40)&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;12&quot;&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;0&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot; letter-spacing=&quot;1&quot;&amp;gt;FEATURES&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;22&quot; fill=&quot;#2563EB&quot; font-weight=&quot;600&quot;&amp;gt;A =&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;22&quot; fill=&quot;#1A1A1A&quot;&amp;gt;[0.9, 0.1, 0.2]&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;42&quot; fill=&quot;#2563EB&quot; font-weight=&quot;600&quot;&amp;gt;B =&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;42&quot; fill=&quot;#1A1A1A&quot;&amp;gt;[0.95, 0.05, 0.15]&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;62&quot; fill=&quot;#B8651A&quot; font-weight=&quot;600&quot;&amp;gt;C =&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;62&quot; fill=&quot;#1A1A1A&quot;&amp;gt;[0.1, 0.85, 0.2]&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;82&quot; fill=&quot;#059669&quot; font-weight=&quot;600&quot;&amp;gt;D =&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;30&quot; y=&quot;82&quot; fill=&quot;#1A1A1A&quot;&amp;gt;[0.3, 0.3, 0.8]&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;115&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot; letter-spacing=&quot;1&quot;&amp;gt;DOT PRODUCTS&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;137&quot; fill=&quot;#1A1A1A&quot;&amp;gt;A·B = 0.89&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;155&quot; fill=&quot;#1A1A1A&quot;&amp;gt;A·C = 0.21&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;173&quot; fill=&quot;#1A1A1A&quot;&amp;gt;A·D = 0.46&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;191&quot; fill=&quot;#1A1A1A&quot;&amp;gt;B·C = 0.17&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;209&quot; fill=&quot;#1A1A1A&quot;&amp;gt;B·D = 0.42&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;227&quot; fill=&quot;#1A1A1A&quot;&amp;gt;C·D = 0.44&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(440,40)&quot;&amp;gt;
&amp;lt;text x=&quot;90&quot; y=&quot;0&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot; letter-spacing=&quot;1&quot;&amp;gt;GRAM MATRIX&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;55&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;13&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;A&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;90&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;13&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;B&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;125&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;13&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;C&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;160&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;13&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;D&amp;lt;/text&amp;gt;
&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot;&amp;gt;
&amp;lt;text x=&quot;30&quot; y=&quot;48&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;A&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;32&quot; width=&quot;30&quot; height=&quot;22&quot; fill=&quot;#2563EB&quot;/&amp;gt;&amp;lt;text x=&quot;55&quot; y=&quot;48&quot; text-anchor=&quot;middle&quot; fill=&quot;#FFFFFF&quot;&amp;gt;1.0&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;75&quot; y=&quot;32&quot; width=&quot;30&quot; height=&quot;22&quot; fill=&quot;#2563EB&quot; opacity=&quot;.85&quot;/&amp;gt;&amp;lt;text x=&quot;90&quot; y=&quot;48&quot; text-anchor=&quot;middle&quot; fill=&quot;#FFFFFF&quot;&amp;gt;0.89&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;110&quot; y=&quot;32&quot; width=&quot;30&quot; height=&quot;22&quot; fill=&quot;#2563EB&quot; opacity=&quot;.18&quot;/&amp;gt;&amp;lt;text x=&quot;125&quot; y=&quot;48&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;0.21&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;145&quot; y=&quot;32&quot; width=&quot;30&quot; height=&quot;22&quot; fill=&quot;#2563EB&quot; opacity=&quot;.42&quot;/&amp;gt;&amp;lt;text x=&quot;160&quot; y=&quot;48&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;0.46&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;30&quot; y=&quot;72&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;B&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;56&quot; width=&quot;30&quot; height=&quot;22&quot; fill=&quot;#2563EB&quot; opacity=&quot;.85&quot;/&amp;gt;&amp;lt;text x=&quot;55&quot; y=&quot;72&quot; text-anchor=&quot;middle&quot; fill=&quot;#FFFFFF&quot;&amp;gt;0.89&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;75&quot; y=&quot;56&quot; width=&quot;30&quot; height=&quot;22&quot; fill=&quot;#2563EB&quot;/&amp;gt;&amp;lt;text x=&quot;90&quot; y=&quot;72&quot; text-anchor=&quot;middle&quot; fill=&quot;#FFFFFF&quot;&amp;gt;1.0&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;110&quot; y=&quot;56&quot; width=&quot;30&quot; height=&quot;22&quot; fill=&quot;#2563EB&quot; opacity=&quot;.15&quot;/&amp;gt;&amp;lt;text x=&quot;125&quot; y=&quot;72&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;0.17&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;145&quot; y=&quot;56&quot; width=&quot;30&quot; height=&quot;22&quot; fill=&quot;#2563EB&quot; opacity=&quot;.38&quot;/&amp;gt;&amp;lt;text x=&quot;160&quot; y=&quot;72&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;0.42&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;30&quot; y=&quot;96&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;C&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;80&quot; width=&quot;30&quot; height=&quot;22&quot; fill=&quot;#2563EB&quot; opacity=&quot;.18&quot;/&amp;gt;&amp;lt;text x=&quot;55&quot; y=&quot;96&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;0.21&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;75&quot; y=&quot;80&quot; width=&quot;30&quot; height=&quot;22&quot; fill=&quot;#2563EB&quot; opacity=&quot;.15&quot;/&amp;gt;&amp;lt;text x=&quot;90&quot; y=&quot;96&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;0.17&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;110&quot; y=&quot;80&quot; width=&quot;30&quot; height=&quot;22&quot; fill=&quot;#2563EB&quot;/&amp;gt;&amp;lt;text x=&quot;125&quot; y=&quot;96&quot; text-anchor=&quot;middle&quot; fill=&quot;#FFFFFF&quot;&amp;gt;1.0&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;145&quot; y=&quot;80&quot; width=&quot;30&quot; height=&quot;22&quot; fill=&quot;#2563EB&quot; opacity=&quot;.4&quot;/&amp;gt;&amp;lt;text x=&quot;160&quot; y=&quot;96&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;0.44&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;30&quot; y=&quot;120&quot; font-weight=&quot;600&quot; fill=&quot;#1A1A1A&quot;&amp;gt;D&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;104&quot; width=&quot;30&quot; height=&quot;22&quot; fill=&quot;#2563EB&quot; opacity=&quot;.42&quot;/&amp;gt;&amp;lt;text x=&quot;55&quot; y=&quot;120&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;0.46&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;75&quot; y=&quot;104&quot; width=&quot;30&quot; height=&quot;22&quot; fill=&quot;#2563EB&quot; opacity=&quot;.38&quot;/&amp;gt;&amp;lt;text x=&quot;90&quot; y=&quot;120&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;0.42&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;110&quot; y=&quot;104&quot; width=&quot;30&quot; height=&quot;22&quot; fill=&quot;#2563EB&quot; opacity=&quot;.4&quot;/&amp;gt;&amp;lt;text x=&quot;125&quot; y=&quot;120&quot; text-anchor=&quot;middle&quot; fill=&quot;#1A1A1A&quot;&amp;gt;0.44&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;145&quot; y=&quot;104&quot; width=&quot;30&quot; height=&quot;22&quot; fill=&quot;#2563EB&quot;/&amp;gt;&amp;lt;text x=&quot;160&quot; y=&quot;120&quot; text-anchor=&quot;middle&quot; fill=&quot;#FFFFFF&quot;&amp;gt;1.0&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;0&quot; y=&quot;162&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;
&amp;lt;tspan x=&quot;0&quot; dy=&quot;0&quot;&amp;gt;A·B is high (both sky).&amp;lt;/tspan&amp;gt;
&amp;lt;tspan x=&quot;0&quot; dy=&quot;16&quot;&amp;gt;A·C is low (sky vs fur).&amp;lt;/tspan&amp;gt;
&amp;lt;tspan x=&quot;0&quot; dy=&quot;16&quot;&amp;gt;Diagonal is always 1 (a vector&apos;s&amp;lt;/tspan&amp;gt;
&amp;lt;tspan x=&quot;0&quot; dy=&quot;14&quot;&amp;gt;similarity to itself, assuming it&apos;s&amp;lt;/tspan&amp;gt;
&amp;lt;tspan x=&quot;0&quot; dy=&quot;14&quot;&amp;gt;a unit vector).&amp;lt;/tspan&amp;gt;
&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 4.&amp;lt;/strong&amp;gt; The Gram matrix of four patch features. Each cell (i, j) is the dot product of features i and j. It&apos;s a square, symmetric table that records &lt;em&gt;relationships&lt;/em&gt; rather than values. Two patches that mean similar things will have a high dot product; two patches that mean different things will have a low one. The matrix is the model&apos;s picture of how every part of the image relates to every other part.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;A few things worth noting about this object. The Gram matrix is square: if you have &lt;em&gt;n&lt;/em&gt; vectors it&apos;s &lt;em&gt;n&lt;/em&gt;-by-&lt;em&gt;n&lt;/em&gt;. It&apos;s symmetric (dot products don&apos;t care about order), so entry (i, j) equals entry (j, i). The diagonal entries are each vector&apos;s similarity to itself, which, if the vectors are normalized to unit length, is always 1.&lt;/p&gt;
&lt;p&gt;Here&apos;s the critical property. The Gram matrix doesn&apos;t care about the &lt;em&gt;magnitudes&lt;/em&gt; or &lt;em&gt;orientations&lt;/em&gt; of the underlying features. If you rotated all your feature vectors by the same angle in feature space, the Gram matrix would be unchanged. If you scaled them all by the same factor, it would change in a uniform, predictable way. What the Gram matrix captures is the &lt;em&gt;relational structure&lt;/em&gt;: the geometry of the feature cloud, not its location.&lt;/p&gt;
&lt;p&gt;For a vision model, the Gram matrix of the dense features tells you which patches the model thinks are similar and which it thinks are different. If the matrix is &quot;clean&quot; (high values for patches of the same content, low values for patches of different content), the model has a good local map. If the matrix is &quot;noisy&quot; (random-looking similarity everywhere), the model has lost its map.&lt;/p&gt;
&lt;p&gt;Dense feature collapse, when you plot the Gram matrix of a late-training ViT, looks exactly like static on an old TV. Every patch looks moderately similar to every other patch. The fine distinctions are gone.&lt;/p&gt;
&lt;h3&gt;8. Gram anchoring: the blueprint trick&lt;/h3&gt;
&lt;p&gt;Now we have everything we need to understand Gram anchoring. The DINOv3 team observed the collapse, and they had a specific observation: &lt;em&gt;the Gram matrix used to look fine, earlier in training.&lt;/em&gt; At iteration 100-200k or so, the dense features were sharp, the Gram matrix was clean, the model was doing the right thing. The collapse happened later, as the global objective started to dominate.&lt;/p&gt;
&lt;p&gt;What if you could hold on to the early, healthy Gram matrix? What if, during late-stage training, you added a new loss term that said &quot;student, your current Gram matrix should stay close to the Gram matrix of your own past self&quot;?&lt;/p&gt;
&lt;p&gt;This is Gram anchoring. The details:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Take a snapshot of the model at an earlier training checkpoint, chosen while the dense features are still sharp, before collapse has set in. Call this the &lt;em&gt;Gram teacher&lt;/em&gt;. Hold it fixed for now. (It will be refreshed periodically, every 10k iterations or so, by copying in the current EMA teacher&apos;s weights. The early-snapshot intuition is what matters; the refresh schedule is a practical detail.)&lt;/li&gt;
&lt;li&gt;During later training, on each batch, run the image through both the current student &lt;em&gt;and&lt;/em&gt; the current Gram teacher. Compute both Gram matrices: $G_S$ and $G_T$.&lt;/li&gt;
&lt;li&gt;Add a new term to the loss: the Frobenius norm of the difference between the two Gram matrices. Written out, it&apos;s $|G_S - G_T|^2_F$, which is just &quot;sum up the squared differences between corresponding cells.&quot;&lt;/li&gt;
&lt;li&gt;Train as normal. The student keeps its DINO and iBOT losses, but now also has to keep its Gram matrix from drifting away from the frozen snapshot.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It&apos;s worth pausing on why this works, because the logic is subtle. At first glance, anchoring a newer model to an older version of itself sounds like it would prevent learning. You&apos;re telling the model &quot;be like your dumber self.&quot; Shouldn&apos;t that stop the model from improving?&lt;/p&gt;
&lt;p&gt;It doesn&apos;t, and the reason is that Gram anchoring &lt;em&gt;doesn&apos;t constrain the features themselves&lt;/em&gt;. It constrains only the &lt;em&gt;relationships&lt;/em&gt; between features: the Gram matrix. The underlying feature vectors are free to evolve wildly. They can rotate, they can stretch, they can reshape themselves to capture new concepts. As long as the pairwise dot products stay consistent with the snapshot, the anchoring is satisfied.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Keep the blueprint. Upgrade the materials.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A good analogy: imagine a building. The Gram matrix is the blueprint: which walls are load-bearing, which rooms connect to which, how far the bathroom is from the kitchen. The individual features are the materials: the actual drywall, the carpet, the lighting fixtures. You can swap every piece of drywall for marble, change all the lighting, repaint every wall, and the blueprint is unchanged. The structure remains.&lt;/p&gt;
&lt;p&gt;That&apos;s why the technique works. The early-training dense features had good structure. The Gram matrix was clean, distinct patches had low similarity, similar patches had high similarity. By preserving that structure during late training, you let the features themselves keep getting richer and more semantically loaded, while refusing to let them dissolve into an undifferentiated soup.&lt;/p&gt;
&lt;p&gt;The result, measured on every dense benchmark that exists, is dramatic. Without Gram anchoring, ADE20k segmentation quality peaks early and then falls by tens of mIoU points over the remainder of training: the classic collapse. With Gram anchoring, it keeps climbing and settles at state-of-the-art levels. This is with the exact same backbone, the same data, the same number of iterations. The only difference is one extra loss term that keeps the spatial blueprint intact.&lt;/p&gt;
&lt;p&gt;Gram anchoring is also the reason DINOv3 can be used &lt;em&gt;frozen&lt;/em&gt;, with no fine-tuning, on a dizzying array of downstream tasks. Video tracking without ever seeing a video. Satellite canopy-height estimation that beats purpose-built Earth-observation networks. Medical pathology on atypical mitotic figures. The features are so spatially clean that they just transfer. You drop them into a new task, add a small linear head, and the result is often state of the art.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Checkpoint: what you know now.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vision models produce two kinds of features:&lt;/strong&gt; a global summary and a per-patch dense map. Both matter. Historically, scaling a self-supervised ViT improved one at the expense of the other.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The failure mode is called dense feature collapse:&lt;/strong&gt; the pressure to group patches by semantic content ends up dissolving the per-patch distinctions that dense tasks need.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DINOv3 fixes it with Gram anchoring:&lt;/strong&gt; during late-stage training, use an earlier, still-healthy checkpoint as a Gram teacher, and train the model to keep its current Gram matrix close to that teacher&apos;s. This preserves the &lt;em&gt;structure&lt;/em&gt; of the patch features without freezing the features themselves.&lt;/p&gt;
&lt;p&gt;DINOv3 is now the single strongest vision backbone in the world. But it has one big limitation: it was trained without text, so it has no idea what any object is &lt;em&gt;called&lt;/em&gt;. That&apos;s the next problem.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Act 2 — The Prism&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Why three specialist backbones can&apos;t coexist at inference, and how C-RADIOv4 fuses them into one.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;9. Three teachers, one student&lt;/h3&gt;
&lt;p&gt;DINOv3 is the best dense-feature model ever made. It is also, in one specific way, useless: you can&apos;t talk to it. You can&apos;t ask &quot;is there a dog in this picture?&quot; and get a yes-or-no answer, because DINOv3 has never seen the word &quot;dog.&quot; It has no alignment with language at all. It was trained on pure pixels, without captions or labels.&lt;/p&gt;
&lt;p&gt;For anything involving natural language (zero-shot classification, text-to-image retrieval, visual question answering, multimodal assistants), you need a different kind of model. The standard is &lt;em&gt;SigLIP2&lt;/em&gt;, Google&apos;s 2025 image-text model. Conceptually it sits in the lineage of OpenAI&apos;s CLIP (dual image and text encoders aligned in a shared space), but it&apos;s trained with a different loss: a pairwise sigmoid objective that treats each (image, caption) pair as an independent yes-or-no match. The result is the same kind of shared space. Image features and caption features live in the same embedding space, and you can do a dot product between them to measure how well a caption describes an image.&lt;/p&gt;
&lt;p&gt;SigLIP2 is excellent at language alignment. It&apos;s also, in a different specific way, awkward for dense work. The standard high-resolution variant C-RADIOv4 distills from, SigLIP2-g-384, uses patch-16 tokenization at 384 pixels, which gives a 24×24 feature grid. Fine for classification, coarse for anything pixel-precise. SigLIP 2 does include a newer &quot;NaFlex&quot; variant that supports native aspect ratios and variable resolutions, but the fixed-resolution teacher is what&apos;s in play here. And SigLIP2&apos;s features don&apos;t preserve the sharp spatial geometry DINOv3 gives you. They&apos;re semantically rich but spatially mushy.&lt;/p&gt;
&lt;p&gt;So: DINOv3 has dense features, no text. SigLIP2 has text, no dense features. Surely we also need high-resolution masking (you know, segment-anything), and that&apos;s a whole other specialist: &lt;em&gt;SAM3&lt;/em&gt;, Meta&apos;s November 2025 segmentation model. (A point release, SAM 3.1, landed in March 2026 with tracking speed improvements but no architectural change to the backbone, which is what matters here.) SAM3 will happily take a text prompt and return pixel-precise masks for every instance in an image. But it&apos;s a segmenter, not a general-purpose encoder. It doesn&apos;t give you good global features for classification.&lt;/p&gt;
&lt;p&gt;SAM3 and DINOv3 look redundant at first glance. They both produce dense features, so if DINOv3 is already excellent at dense work, why do we need SAM3 as a separate teacher? Because the two models come from completely different supervision regimes, and each regime teaches something the other one structurally can&apos;t. DINOv3 is self-supervised: it never sees a label, and certainly never sees a mask. Its loss is a clustering objective that pushes patches of similar content toward the same region of feature space. That&apos;s great for semantic grouping, but it&apos;s blind to where one instance of a class ends and another begins, because the loss has no ground truth for instance identity. Two cars parked next to each other sit in the same region of DINOv3&apos;s feature space, by design.&lt;/p&gt;
&lt;p&gt;SAM3 was trained on pixel-exact mask supervision at scale, roughly 4 million unique concepts from Meta&apos;s SA-Co data engine (with instance masks, hard negatives, and both image and video annotations). Every training example carries explicit information about where a specific instance &lt;em&gt;stops&lt;/em&gt;, which is information self-supervision cannot produce no matter how much data you scale to. SAM3&apos;s architecture is built around this: its DETR-based detector has a dedicated &lt;em&gt;presence head&lt;/em&gt; that separates &quot;is this concept in the image&quot; from &quot;where exactly is each copy of it.&quot; The backbone (Meta&apos;s Perception Encoder) is pre-trained separately and then coupled to the detector, with a later tracker stage that runs on top of a frozen backbone.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 280&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; height=&quot;auto&quot;&amp;gt;
&amp;lt;g transform=&quot;translate(205, 28)&quot;&amp;gt;
&amp;lt;circle cx=&quot;0&quot; cy=&quot;0&quot; r=&quot;5&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;text x=&quot;12&quot; y=&quot;4&quot; font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1A1A1A&quot;&amp;gt;patches from car 1&amp;lt;/text&amp;gt;
&amp;lt;circle cx=&quot;160&quot; cy=&quot;0&quot; r=&quot;5&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;text x=&quot;172&quot; y=&quot;4&quot; font-family=&quot;serif&quot; font-size=&quot;13&quot; fill=&quot;#1A1A1A&quot;&amp;gt;patches from car 2&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(30, 60)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;290&quot; height=&quot;200&quot; fill=&quot;#F9F5EB&quot; stroke=&quot;#C9BEAA&quot; stroke-width=&quot;1&quot; rx=&quot;4&quot;/&amp;gt;
&amp;lt;ellipse cx=&quot;145&quot; cy=&quot;95&quot; rx=&quot;92&quot; ry=&quot;48&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;1.5&quot; opacity=&quot;0.9&quot;/&amp;gt;
&amp;lt;circle cx=&quot;100&quot; cy=&quot;78&quot; r=&quot;3.5&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;circle cx=&quot;118&quot; cy=&quot;85&quot; r=&quot;3.5&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;circle cx=&quot;130&quot; cy=&quot;72&quot; r=&quot;3.5&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;circle cx=&quot;135&quot; cy=&quot;92&quot; r=&quot;3.5&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;circle cx=&quot;148&quot; cy=&quot;80&quot; r=&quot;3.5&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;circle cx=&quot;162&quot; cy=&quot;95&quot; r=&quot;3.5&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;circle cx=&quot;172&quot; cy=&quot;84&quot; r=&quot;3.5&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;circle cx=&quot;184&quot; cy=&quot;102&quot; r=&quot;3.5&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;circle cx=&quot;108&quot; cy=&quot;102&quot; r=&quot;3.5&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;circle cx=&quot;125&quot; cy=&quot;110&quot; r=&quot;3.5&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;circle cx=&quot;155&quot; cy=&quot;108&quot; r=&quot;3.5&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;circle cx=&quot;178&quot; cy=&quot;115&quot; r=&quot;3.5&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;circle cx=&quot;122&quot; cy=&quot;100&quot; r=&quot;3.5&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;circle cx=&quot;168&quot; cy=&quot;112&quot; r=&quot;3.5&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;circle cx=&quot;145&quot; cy=&quot;118&quot; r=&quot;3.5&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;circle cx=&quot;140&quot; cy=&quot;85&quot; r=&quot;3.5&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;text x=&quot;145&quot; y=&quot;165&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;16&quot; font-weight=&quot;600&quot; fill=&quot;#2563EB&quot;&amp;gt;DINOv3&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;145&quot; y=&quot;184&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;12&quot; fill=&quot;#6B6258&quot; font-style=&quot;italic&quot;&amp;gt;one cluster. both cars blur together.&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g transform=&quot;translate(360, 60)&quot;&amp;gt;
&amp;lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;290&quot; height=&quot;200&quot; fill=&quot;#F9F5EB&quot; stroke=&quot;#C9BEAA&quot; stroke-width=&quot;1&quot; rx=&quot;4&quot;/&amp;gt;
&amp;lt;ellipse cx=&quot;85&quot; cy=&quot;85&quot; rx=&quot;52&quot; ry=&quot;30&quot; fill=&quot;#EDE0FC&quot; stroke=&quot;#9333EA&quot; stroke-width=&quot;1.5&quot; opacity=&quot;0.9&quot;/&amp;gt;
&amp;lt;ellipse cx=&quot;215&quot; cy=&quot;105&quot; rx=&quot;52&quot; ry=&quot;30&quot; fill=&quot;#EDE0FC&quot; stroke=&quot;#9333EA&quot; stroke-width=&quot;1.5&quot; opacity=&quot;0.9&quot;/&amp;gt;
&amp;lt;text x=&quot;85&quot; y=&quot;48&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#9333EA&quot;&amp;gt;instance 1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;215&quot; y=&quot;145&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#9333EA&quot;&amp;gt;instance 2&amp;lt;/text&amp;gt;
&amp;lt;circle cx=&quot;65&quot; cy=&quot;80&quot; r=&quot;3.5&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;circle cx=&quot;78&quot; cy=&quot;88&quot; r=&quot;3.5&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;circle cx=&quot;85&quot; cy=&quot;75&quot; r=&quot;3.5&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;circle cx=&quot;95&quot; cy=&quot;90&quot; r=&quot;3.5&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;circle cx=&quot;102&quot; cy=&quot;82&quot; r=&quot;3.5&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;circle cx=&quot;72&quot; cy=&quot;95&quot; r=&quot;3.5&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;circle cx=&quot;92&quot; cy=&quot;96&quot; r=&quot;3.5&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;circle cx=&quot;108&quot; cy=&quot;92&quot; r=&quot;3.5&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;circle cx=&quot;195&quot; cy=&quot;100&quot; r=&quot;3.5&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;circle cx=&quot;208&quot; cy=&quot;108&quot; r=&quot;3.5&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;circle cx=&quot;215&quot; cy=&quot;95&quot; r=&quot;3.5&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;circle cx=&quot;225&quot; cy=&quot;110&quot; r=&quot;3.5&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;circle cx=&quot;232&quot; cy=&quot;102&quot; r=&quot;3.5&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;circle cx=&quot;200&quot; cy=&quot;115&quot; r=&quot;3.5&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;circle cx=&quot;222&quot; cy=&quot;118&quot; r=&quot;3.5&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;circle cx=&quot;240&quot; cy=&quot;112&quot; r=&quot;3.5&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;text x=&quot;145&quot; y=&quot;165&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;16&quot; font-weight=&quot;600&quot; fill=&quot;#9333EA&quot;&amp;gt;SAM3&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;145&quot; y=&quot;184&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;12&quot; fill=&quot;#6B6258&quot; font-style=&quot;italic&quot;&amp;gt;two clusters. instance identity preserved.&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 5.&amp;lt;/strong&amp;gt; What each teacher&apos;s features encode for the same two-car scene. DINOv3&apos;s self-supervised clustering loss puts all &quot;car&quot; patches near each other, so the two cars&apos; patches interleave in one region. SAM3&apos;s mask supervision pushes patches from different instance masks to different regions, so the two cars form separate clusters. DINOv3 carries &quot;what is it.&quot; SAM3 carries &quot;which one is it.&quot; C-RADIOv4 inherits both.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;This matters for C-RADIOv4 in two ways. First, the student&apos;s dense features inherit information that&apos;s structurally absent from the DINOv3 signal: instance-level discriminability, and features shaped by a pixel-exact boundary loss. Second, because the student is distilled specifically to match the Perception Encoder&apos;s output format, C-RADIOv4 can function as a drop-in replacement for SAM3&apos;s backbone. Swap it in, keep SAM3&apos;s detector and tracker unchanged, and you get the whole promptable-segmentation and video-tracking stack at a fraction of the encoder cost. That kind of compatibility is at least part of the appeal of SAM3 as a teacher.&lt;/p&gt;
&lt;p&gt;Three backbones. Three complementary strengths. And running all three in production is expensive enough that most teams won&apos;t do it. A ViT-H at 1024 pixels isn&apos;t cheap on its own; running two or three of them in parallel multiplies your inference cost for every request, and multi-encoder VLM stacks are the kind of thing you build reluctantly rather than by design. Getting the same capabilities out of one backbone is worth real money.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 270&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; height=&quot;auto&quot;&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;circle cx=&quot;120&quot; cy=&quot;120&quot; r=&quot;80&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;2.5&quot;/&amp;gt;
&amp;lt;text x=&quot;120&quot; y=&quot;105&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;22&quot; font-weight=&quot;700&quot; fill=&quot;#1A1A1A&quot;&amp;gt;DINOv3&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;120&quot; y=&quot;125&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-style=&quot;italic&quot; fill=&quot;#2563EB&quot;&amp;gt;self-supervised&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;120&quot; y=&quot;220&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;12&quot; fill=&quot;#1A1A1A&quot;&amp;gt;+ dense patch geometry&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;120&quot; y=&quot;240&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;12&quot; fill=&quot;#6B6258&quot;&amp;gt;− no text grounding&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;circle cx=&quot;340&quot; cy=&quot;120&quot; r=&quot;80&quot; fill=&quot;#D1FAE5&quot; stroke=&quot;#059669&quot; stroke-width=&quot;2.5&quot;/&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;105&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;22&quot; font-weight=&quot;700&quot; fill=&quot;#1A1A1A&quot;&amp;gt;SigLIP2&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;125&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-style=&quot;italic&quot; fill=&quot;#059669&quot;&amp;gt;text-aligned&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;220&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;12&quot; fill=&quot;#1A1A1A&quot;&amp;gt;+ zero-shot labels&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;340&quot; y=&quot;240&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;12&quot; fill=&quot;#6B6258&quot;&amp;gt;− locked at 384px&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;circle cx=&quot;560&quot; cy=&quot;120&quot; r=&quot;80&quot; fill=&quot;#EDE0FC&quot; stroke=&quot;#9333EA&quot; stroke-width=&quot;2.5&quot;/&amp;gt;
&amp;lt;text x=&quot;560&quot; y=&quot;105&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;22&quot; font-weight=&quot;700&quot; fill=&quot;#1A1A1A&quot;&amp;gt;SAM3&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;560&quot; y=&quot;125&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-style=&quot;italic&quot; fill=&quot;#9333EA&quot;&amp;gt;fully supervised&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;560&quot; y=&quot;220&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;12&quot; fill=&quot;#1A1A1A&quot;&amp;gt;+ instance-aware masks&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;560&quot; y=&quot;240&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;12&quot; fill=&quot;#6B6258&quot;&amp;gt;− narrow semantics&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;text x=&quot;230&quot; y=&quot;110&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#B91C1C&quot;&amp;gt;LATENCY&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;210&quot; y1=&quot;120&quot; x2=&quot;250&quot; y2=&quot;120&quot; stroke=&quot;#B91C1C&quot; stroke-width=&quot;1.5&quot; stroke-dasharray=&quot;4,3&quot;/&amp;gt;
&amp;lt;text x=&quot;450&quot; y=&quot;110&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#B91C1C&quot;&amp;gt;LATENCY&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;430&quot; y1=&quot;120&quot; x2=&quot;470&quot; y2=&quot;120&quot; stroke=&quot;#B91C1C&quot; stroke-width=&quot;1.5&quot; stroke-dasharray=&quot;4,3&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 6.&amp;lt;/strong&amp;gt; Three specialized backbones, each excellent at something, each deficient at something else. Running them together at inference time would give us a complete vision system, and also a multi-GPU deployment burden nobody can justify. The question: can we train a single student that gets all three of their strengths?&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;This is where C-RADIOv4 enters the story.&lt;/p&gt;
&lt;h3&gt;10. What distillation is, really&lt;/h3&gt;
&lt;p&gt;Before we can talk about C-RADIOv4, one more piece of background. The technique C-RADIOv4 relies on is called &lt;em&gt;knowledge distillation&lt;/em&gt;, and it deserves a careful explanation because the whole story turns on it.&lt;/p&gt;
&lt;p&gt;The simplest version of distillation was introduced by Geoffrey Hinton in 2015. You have a big, slow model (the &lt;em&gt;teacher&lt;/em&gt;) that&apos;s been trained on some task. You want a small, fast model (the &lt;em&gt;student&lt;/em&gt;) that does the same task almost as well. Training the small model from scratch on the original labels gives you mediocre results. But if you use the teacher&apos;s &lt;em&gt;outputs&lt;/em&gt; as soft targets for the student, the student does dramatically better than it would if it were just mimicking the ground-truth labels.&lt;/p&gt;
&lt;p&gt;The reason is that the teacher&apos;s outputs contain more information than the labels do. A label is a single correct answer. A teacher&apos;s output is a probability distribution over all possible answers, which encodes relationships: &quot;this looks 80% like a cat, 15% like a lynx, 5% like a fox, nothing else.&quot; The student learns the teacher&apos;s full landscape of what&apos;s similar to what, not only the right answer.&lt;/p&gt;
&lt;p&gt;Now: what if, instead of distilling from one teacher, we distill from several? What if we have a set of specialist teachers, each expert at a different aspect of vision, and we train a student whose job is to match all of their outputs simultaneously?&lt;/p&gt;
&lt;p&gt;This is &lt;em&gt;multi-teacher distillation&lt;/em&gt;, and it&apos;s the core idea behind the RADIO family of models (of which C-RADIOv4 is the latest). The student doesn&apos;t try to recreate any single teacher. It learns a &lt;em&gt;unified embedding&lt;/em&gt; (a single feature vector) from which all the teachers&apos; output features can be predicted. Small heads on top of the student are trained to reproduce DINOv3&apos;s output, SigLIP2&apos;s output, and SAM3&apos;s output, respectively. But the backbone itself is shared.&lt;/p&gt;
&lt;p&gt;Crucially, this is an &lt;em&gt;agglomerative&lt;/em&gt; process: the student ends up carrying information from all three teachers, because it has to serve all three of their prediction heads. At inference time, you run the student once (one forward pass) and you get features that can be used for any of the downstream tasks. If you need DINOv3-style dense geometry, read off the dense features. If you need SigLIP2-style text alignment, pass through the SigLIP2 head. If you need SAM3-style segmentation, pass through the SAM3 head. The expensive forward pass happens once. The lightweight heads run in microseconds.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 260&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; height=&quot;auto&quot;&amp;gt;
&amp;lt;g font-family=&quot;serif&quot; font-size=&quot;14&quot; font-weight=&quot;600&quot;&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;30&quot; width=&quot;110&quot; height=&quot;40&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;1.5&quot;/&amp;gt;&amp;lt;text x=&quot;85&quot; y=&quot;55&quot; text-anchor=&quot;middle&quot; fill=&quot;#2563EB&quot;&amp;gt;DINOv3&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;110&quot; width=&quot;110&quot; height=&quot;40&quot; fill=&quot;#D1FAE5&quot; stroke=&quot;#059669&quot; stroke-width=&quot;1.5&quot;/&amp;gt;&amp;lt;text x=&quot;85&quot; y=&quot;135&quot; text-anchor=&quot;middle&quot; fill=&quot;#059669&quot;&amp;gt;SigLIP2&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;190&quot; width=&quot;110&quot; height=&quot;40&quot; fill=&quot;#EDE0FC&quot; stroke=&quot;#9333EA&quot; stroke-width=&quot;1.5&quot;/&amp;gt;&amp;lt;text x=&quot;85&quot; y=&quot;215&quot; text-anchor=&quot;middle&quot; fill=&quot;#9333EA&quot;&amp;gt;SAM3&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;85&quot; y=&quot;18&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; letter-spacing=&quot;1.5&quot; fill=&quot;#6B6258&quot;&amp;gt;TEACHERS (FROZEN)&amp;lt;/text&amp;gt;
&amp;lt;g stroke=&quot;#8F8578&quot;&amp;gt;
&amp;lt;line x1=&quot;145&quot; y1=&quot;50&quot; x2=&quot;285&quot; y2=&quot;120&quot; marker-end=&quot;url(#a3)&quot;/&amp;gt;
&amp;lt;line x1=&quot;145&quot; y1=&quot;130&quot; x2=&quot;285&quot; y2=&quot;130&quot; marker-end=&quot;url(#a3)&quot;/&amp;gt;
&amp;lt;line x1=&quot;145&quot; y1=&quot;210&quot; x2=&quot;285&quot; y2=&quot;140&quot; marker-end=&quot;url(#a3)&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;defs&amp;gt;&amp;lt;marker id=&quot;a3&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;5&quot; markerHeight=&quot;5&quot; orient=&quot;auto&quot;&amp;gt;&amp;lt;path d=&quot;M0,0 L10,5 L0,10 z&quot; fill=&quot;#8F8578&quot;/&amp;gt;&amp;lt;/marker&amp;gt;&amp;lt;/defs&amp;gt;
&amp;lt;rect x=&quot;290&quot; y=&quot;100&quot; width=&quot;120&quot; height=&quot;60&quot; fill=&quot;#1A1A1A&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;text x=&quot;350&quot; y=&quot;125&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;15&quot; font-weight=&quot;700&quot; fill=&quot;#F4EEE3&quot;&amp;gt;student&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;350&quot; y=&quot;145&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; font-style=&quot;italic&quot; fill=&quot;#B8651A&quot;&amp;gt;C-RADIOv4&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;412&quot; y1=&quot;130&quot; x2=&quot;432&quot; y2=&quot;130&quot; stroke=&quot;#8F8578&quot; marker-end=&quot;url(#a3)&quot;/&amp;gt;
&amp;lt;rect x=&quot;435&quot; y=&quot;115&quot; width=&quot;100&quot; height=&quot;30&quot; fill=&quot;none&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;485&quot; y=&quot;134&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#1A1A1A&quot;&amp;gt;unified feature&amp;lt;/text&amp;gt;
&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot;&amp;gt;
&amp;lt;line x1=&quot;540&quot; y1=&quot;125&quot; x2=&quot;565&quot; y2=&quot;55&quot; stroke=&quot;#8F8578&quot; marker-end=&quot;url(#a3)&quot;/&amp;gt;
&amp;lt;rect x=&quot;565&quot; y=&quot;40&quot; width=&quot;100&quot; height=&quot;30&quot; fill=&quot;#DBEAFE&quot; stroke=&quot;#2563EB&quot;/&amp;gt;&amp;lt;text x=&quot;615&quot; y=&quot;59&quot; text-anchor=&quot;middle&quot; fill=&quot;#2563EB&quot;&amp;gt;DINOv3 head&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;540&quot; y1=&quot;130&quot; x2=&quot;565&quot; y2=&quot;130&quot; stroke=&quot;#8F8578&quot; marker-end=&quot;url(#a3)&quot;/&amp;gt;
&amp;lt;rect x=&quot;565&quot; y=&quot;115&quot; width=&quot;100&quot; height=&quot;30&quot; fill=&quot;#D1FAE5&quot; stroke=&quot;#059669&quot;/&amp;gt;&amp;lt;text x=&quot;615&quot; y=&quot;134&quot; text-anchor=&quot;middle&quot; fill=&quot;#059669&quot;&amp;gt;SigLIP head&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;540&quot; y1=&quot;135&quot; x2=&quot;565&quot; y2=&quot;205&quot; stroke=&quot;#8F8578&quot; marker-end=&quot;url(#a3)&quot;/&amp;gt;
&amp;lt;rect x=&quot;565&quot; y=&quot;190&quot; width=&quot;100&quot; height=&quot;30&quot; fill=&quot;#EDE0FC&quot; stroke=&quot;#9333EA&quot;/&amp;gt;&amp;lt;text x=&quot;615&quot; y=&quot;209&quot; text-anchor=&quot;middle&quot; fill=&quot;#9333EA&quot;&amp;gt;SAM3 head&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 7.&amp;lt;/strong&amp;gt; Multi-teacher distillation in action. Three frozen teachers produce outputs for an image. A shared student backbone learns a single unified feature, and three small heads, one per teacher, are trained to reproduce each teacher&apos;s output from that feature. At inference, the student runs once; the heads run as needed.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;This all sounds elegant and easy. In practice, naive multi-teacher distillation with teachers that have very different activation statistics runs into a known failure mode: the student disproportionately learns the teacher with the loudest loss and underfits the quieter ones. The PHI-S paper documents this quantitatively. Text alignment capabilities are the typical casualty when DINO-style teachers are in the mix, because their feature distributions are much wider.&lt;/p&gt;
&lt;p&gt;The reason, it turned out, was geometry.&lt;/p&gt;
&lt;h3&gt;11. Why three teachers clash&lt;/h3&gt;
&lt;p&gt;Let&apos;s go back to feature space. Imagine DINOv3&apos;s output features as a cloud of points in a high-dimensional space. Each image produces one point. The cloud as a whole occupies some region of the space. How &lt;em&gt;big&lt;/em&gt; is that region? How spread out are the points?&lt;/p&gt;
&lt;p&gt;This property (how widely a set of feature vectors spreads out in its space) has a technical name: &lt;em&gt;angular dispersion&lt;/em&gt;. You can measure it empirically by taking a large batch of images, encoding each one, and computing the average angle between every pair of features. A cloud that&apos;s tightly clustered has low angular dispersion. A cloud that fans out widely has high angular dispersion.&lt;/p&gt;
&lt;p&gt;Here are the measured values, from the C-RADIOv4 paper:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DINOv3-7B&lt;/strong&gt; — dispersion $\approx 2.186$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SigLIP2-g&lt;/strong&gt; — dispersion $\approx 0.694$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DINOv3&apos;s features spread over roughly three times as much angular volume as SigLIP2&apos;s. The geometric analogy: DINOv3 is a floodlight, casting a broad cone. SigLIP2 is a laser pointer, emitting a narrow beam.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 280&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; height=&quot;auto&quot;&amp;gt;
&amp;lt;circle cx=&quot;340&quot; cy=&quot;80&quot; r=&quot;5&quot; fill=&quot;#1A1A1A&quot;/&amp;gt;
&amp;lt;text x=&quot;355&quot; y=&quot;78&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; font-style=&quot;italic&quot; fill=&quot;#6B6258&quot;&amp;gt;shared feature origin&amp;lt;/text&amp;gt;
&amp;lt;path d=&quot;M340,80 L180,250 L500,250 Z&quot; fill=&quot;#2563EB&quot; fill-opacity=&quot;.25&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;path d=&quot;M340,80 L295,250 L385,250 Z&quot; fill=&quot;#059669&quot; fill-opacity=&quot;.4&quot; stroke=&quot;#059669&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;rect x=&quot;130&quot; y=&quot;253&quot; width=&quot;150&quot; height=&quot;22&quot; fill=&quot;#2563EB&quot;/&amp;gt;
&amp;lt;text x=&quot;205&quot; y=&quot;268&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#FFFFFF&quot;&amp;gt;DINOv3: 2.186&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;400&quot; y=&quot;253&quot; width=&quot;150&quot; height=&quot;22&quot; fill=&quot;#059669&quot;/&amp;gt;
&amp;lt;text x=&quot;475&quot; y=&quot;268&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;11&quot; fill=&quot;#FFFFFF&quot;&amp;gt;SigLIP2: 0.694&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 8.&amp;lt;/strong&amp;gt; Two teacher geometries, drawn as cones originating from the shared feature-space origin. DINOv3&apos;s cone is wide; SigLIP2&apos;s is narrow. They live in different regions of the same space, but the DINOv3 cloud occupies roughly 3× the angular volume.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;Why does this kill naive distillation? Because of how distillation losses work. The student is trained to match each teacher&apos;s output. The loss for each teacher is something like &quot;squared distance between student&apos;s prediction and teacher&apos;s target,&quot; averaged across a batch.&lt;/p&gt;
&lt;p&gt;If DINOv3&apos;s features are spread over a wide angular range, the typical distance from one DINOv3 feature to another is large. Large distances mean large loss values. So DINOv3&apos;s loss contribution to the total loss (summed across all teachers) is large. SigLIP2&apos;s features are bunched tightly together, so their distances are small, so their loss contribution is small.&lt;/p&gt;
&lt;p&gt;When you backpropagate, gradient magnitudes scale with loss magnitudes. DINOv3&apos;s gradient dominates. SigLIP2&apos;s gradient gets drowned out. The student optimizes the thing that&apos;s loudest, which is DINOv3, and ignores the thing that&apos;s quiet, which is SigLIP2. End result: you&apos;ve trained a DINOv3 lookalike with a vestigial, useless SigLIP2 head.&lt;/p&gt;
&lt;p&gt;This is a geometric problem, and it has a geometric solution.&lt;/p&gt;
&lt;h3&gt;12. Three ingredients of the C-RADIO recipe&lt;/h3&gt;
&lt;p&gt;The C-RADIOv4 tech report builds on a set of tricks that have been accumulating in the RADIO line for a while. Two of them — PHI-S and shift equivariance — were introduced in earlier RADIO papers (2024) and refined in v4. The third, stochastic multi-resolution training, is the natural culmination of work on the &quot;mode switching&quot; problem that has been chipped away at across several releases. Taken together, they&apos;re the core ingredients that make agglomerative distillation work at all.&lt;/p&gt;
&lt;h4&gt;Ingredient 1: Balanced Summary Loss (PHI-S)&lt;/h4&gt;
&lt;p&gt;Divide each teacher&apos;s loss contribution by that teacher&apos;s angular dispersion. Before the losses get summed, DINOv3&apos;s loss gets scaled down by 2.186 and SigLIP2&apos;s loss gets scaled up (or equivalently, scaled down by 0.694). After this normalization, both teachers contribute the same total gradient magnitude. The floodlight and the laser pull with equal force.&lt;/p&gt;
&lt;p&gt;The exact technique, which NVIDIA calls &lt;em&gt;PHI-S&lt;/em&gt; (for &quot;PHI-Standardization&quot;), goes a bit further than dividing by a single dispersion number. Under the hood it applies a Hadamard rotation to each teacher&apos;s feature space and then standardizes the resulting dimensions uniformly, so that the distribution becomes isotropic: no direction in the feature space dominates any other. The practical effect is the one you&apos;d expect — the student is forced to learn the &lt;em&gt;shape&lt;/em&gt; of each teacher&apos;s feature space, not just its loudest directions — and the &quot;divide by dispersion&quot; framing above captures the spirit even if the mechanics are a little fancier. (Note: PHI-S was introduced earlier in the RADIO line, around v2.5 in 2024. C-RADIOv4 inherits it rather than introducing it.)&lt;/p&gt;
&lt;p&gt;After PHI-S, multi-teacher distillation actually works. The student carries all three teachers&apos; capabilities in a single embedding. But we&apos;re not done. There are still two subtle failure modes that show up when you try to distill from many teachers at once.&lt;/p&gt;
&lt;h4&gt;Ingredient 2: Shift Equivariance&lt;/h4&gt;
&lt;p&gt;Here&apos;s a problem nobody thinks about until they&apos;re deep into multi-teacher distillation: every teacher has &lt;em&gt;bad habits&lt;/em&gt;. Little position-dependent quirks that aren&apos;t really about the image content but are artifacts of how the teacher was trained.&lt;/p&gt;
&lt;p&gt;A famous example in the ViT literature: large self-supervised models tend to produce a handful of &quot;artifact tokens&quot;: patches with anomalously large activations that don&apos;t correspond to anything in the image but instead get used as scratchpad memory for the network. DINOv3 actually addresses this with an architectural trick called &lt;em&gt;register tokens&lt;/em&gt;: four extra learnable tokens prepended to the sequence, specifically designed to absorb the scratchpad role so that the real patch tokens can stay clean. That helps a lot. But it doesn&apos;t eliminate every position-locked quirk. Residual patterns survive in all the teachers. SigLIP2 has dead zones along the borders of its feature maps, because its training data was cropped and the border regions saw systematically less variation. SAM3 has grid-aligned artifacts that come from its windowed attention pattern. DINOv3 still has subtler traces tied to specific coordinates in its positional encoding.&lt;/p&gt;
&lt;p&gt;None of these habits are properties of the input image. They&apos;re properties of the teacher&apos;s architecture and training, anchored to specific pixel coordinates. When you distill naively, the student learns these artifacts faithfully and bakes them into its own features forever.&lt;/p&gt;
&lt;p&gt;The fix: &lt;em&gt;shift equivariance&lt;/em&gt;. During training, take the student&apos;s input and translate it by a random small offset. Shift the image left by 7 pixels, say. The teacher sees the original un-shifted image. When you compare student and teacher outputs, you un-shift the student&apos;s feature map (moving it back to align with the teacher&apos;s) before computing the loss.&lt;/p&gt;
&lt;p&gt;Now think about what these artifacts have in common. The border dead zones are at fixed pixel coordinates. The SAM3 grid artifacts are at fixed pixel coordinates. Any residual position-locked pattern lives at fixed pixel coordinates. In the un-shifted student&apos;s feature map, the &quot;corresponding&quot; patch is at a different pixel coordinate every batch, because the shift offset changes randomly. So the artifacts stop correlating with anything. The student can&apos;t learn to produce them, because there&apos;s no fixed position where they consistently appear. The student gives up memorizing positions and learns only the input-content-dependent part of each teacher&apos;s signal.&lt;/p&gt;
&lt;p&gt;It&apos;s a minimal fix. One line of code during training, and an entire class of inherited garbage disappears from the student&apos;s features.&lt;/p&gt;
&lt;h4&gt;Ingredient 3: Stochastic Multi-Resolution&lt;/h4&gt;
&lt;p&gt;The third ingredient concerns a subtle issue you only notice when you try to use a model at many different resolutions. Standard ViT training happens at a single fixed resolution, usually 224px or 336px. When you deploy the model and someone sends it a 1024px image, you&apos;re asking it to generalize to a regime it never saw.&lt;/p&gt;
&lt;p&gt;What ends up happening, in practice, is the model learns &lt;em&gt;two modes&lt;/em&gt;. One mode works at the training resolution and assumes certain statistical properties of the input (fixed number of patches, fixed receptive field, fixed position indices). Another mode, activated when the image is much larger, has to awkwardly interpolate. The two modes conflict. The same features don&apos;t mean the same thing at 224px and 1024px. Inference becomes brittle. Downstream tasks that need a specific resolution work great; tasks that need a different one suddenly fail.&lt;/p&gt;
&lt;p&gt;C-RADIOv4 solves this by randomly sampling the training resolution on every batch, from a discrete set that spans low resolutions (128, 192, 224, 256, 384, 432) and high resolutions (512, 768, 1024, 1152). The student learns a single representation function that works at every scale. No modes. No special cases. One checkpoint serves any resolution at inference.&lt;/p&gt;
&lt;p&gt;This also has a lovely side effect: it lets C-RADIOv4 track the full scaling curve of DINOv3-7B at a fraction of the parameter count. A 631M-parameter C-RADIOv4-H student matches (and often exceeds) an 840M-parameter DINOv3-H+ on dense benchmarks, precisely because the student has been pressured by multiple teachers to be efficient. It can&apos;t afford to waste parameters on resolution-specific specializations.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Checkpoint: what you know now.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No single vision backbone does everything well.&lt;/strong&gt; DINOv3 has dense self-supervised features but no text and no instance separation. SigLIP2 has text alignment but no dense spatial structure. SAM3 has mask-supervised features with instance separation but narrow global semantics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multi-teacher distillation can fuse them into one student&lt;/strong&gt;, with three key ingredients accumulated across RADIO releases:&lt;/p&gt;
&lt;p&gt;(a) &lt;em&gt;PHI-S normalization&lt;/em&gt; so that the floodlight and the laser contribute equal gradient weight;
(b) &lt;em&gt;shift equivariance&lt;/em&gt; so the student doesn&apos;t inherit position-locked teacher artifacts;
(c) &lt;em&gt;stochastic multi-resolution&lt;/em&gt; so the student learns a single scale-invariant function instead of a per-resolution mode.&lt;/p&gt;
&lt;p&gt;The result is C-RADIOv4: one backbone that runs all three teachers&apos; capabilities in a single forward pass, with 412M or 631M parameters depending on the variant.&lt;/p&gt;
&lt;p&gt;But we have one problem left. The trick only works if all three teachers can produce usable targets at the training resolution. DINOv3 can. SAM3 can. SigLIP2 cannot.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Act 3 — The Dial&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;FeatSharp, a 2D upsampler built on a 3D reconstruction trick.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;13. Resolution, the last mile&lt;/h3&gt;
&lt;p&gt;SigLIP2 was trained at 384×384. Its feature map, at that input resolution, is 24×24, one vector per 16×16 pixel patch. If you ask SigLIP2 to process a 1152×1152 image, its internal machinery gets cranky. Position embeddings, which were learned at the training resolution, now have to extrapolate. The feature map becomes 72×72, but the patches at those new positions have position encodings the model has never seen. The features degrade, not catastrophically, but visibly. Boundaries become fuzzy. Patterns repeat where they shouldn&apos;t. Textures look like plaid.&lt;/p&gt;
&lt;p&gt;You can see it with your eyes if you run a PCA on the feature map and visualize it as a color image. At 384px, SigLIP2&apos;s features look clean, crisp, object-aware. At 1152px via simple bilinear interpolation, they look like a defocused photograph.&lt;/p&gt;
&lt;p&gt;The problem for C-RADIOv4: if the training resolution is random between 128px and 1152px, the student sometimes sees images at 1152px. At that resolution, the student needs to produce features that match SigLIP2&apos;s features. But SigLIP2 can&apos;t produce clean features at 1152px. The supervision signal is noise. The student learns nothing useful from SigLIP2 at high resolutions, which means its text-alignment capabilities stay weak.&lt;/p&gt;
&lt;p&gt;We need a way to get &lt;em&gt;clean&lt;/em&gt; SigLIP2 features at 1152px, without retraining SigLIP2. This is the job of FeatSharp.&lt;/p&gt;
&lt;h3&gt;14. The NeRF trick for features&lt;/h3&gt;
&lt;p&gt;To explain FeatSharp, I have to briefly talk about NeRF. NeRF (Neural Radiance Fields) is a 3D reconstruction technique from 2020. Its setup is this: you have a few dozen 2D photographs of a real-world scene, taken from different viewpoints. You want to reconstruct a 3D representation of the scene that you can render from any new viewpoint.&lt;/p&gt;
&lt;p&gt;NeRF&apos;s core insight is a consistency principle. A real 3D point in the scene is observed in multiple 2D photos. When you look at where that point projects to in photo A, you see a specific color. When you look at where it projects to in photo B (from a different angle), you see that same color, or something close to it, depending on lighting. &lt;em&gt;Features of real 3D structure are consistent across views.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Conversely: anything that &lt;em&gt;varies&lt;/em&gt; across views is not real 3D structure. It&apos;s an artifact: sensor noise, specular reflection, glare, occlusion, something that isn&apos;t part of the stable underlying scene.&lt;/p&gt;
&lt;p&gt;NeRF uses this to build a coherent 3D representation: it finds the 3D structure that, when rendered to the 2D photographs, is consistent with all of them. Parts that can&apos;t be made consistent are rejected as noise.&lt;/p&gt;
&lt;p&gt;Now: the same principle applies, in a totally different setting, to feature upsampling.&lt;/p&gt;
&lt;p&gt;Imagine you have a low-resolution encoder, say SigLIP2 at 384px. You want its feature map at 1152px. Naive bilinear interpolation will hallucinate details the encoder never committed to. But what if you exploit consistency across &lt;em&gt;views of the feature map&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;Here&apos;s the FeatSharp recipe:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Take the input image. Jitter it slightly: shift it by a few pixels, crop it differently, flip it horizontally, mess with its alignment. Generate many small variations (&quot;views&quot;) of the same underlying scene.&lt;/li&gt;
&lt;li&gt;Run the low-resolution encoder on every jittered view. Collect the feature maps. They&apos;re all low-resolution, but they&apos;re all slightly misaligned versions of roughly the same content.&lt;/li&gt;
&lt;li&gt;Learn a high-resolution feature map such that, when you apply the corresponding jitter and downsample to low resolution, the result matches each view&apos;s low-res feature map.&lt;/li&gt;
&lt;li&gt;Features that are consistent across views of the same content survive the upsampling process. Features that flicker, changing from one jitter to the next, get suppressed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 680 260&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; height=&quot;auto&quot;&amp;gt;
&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot;&amp;gt;&amp;lt;text x=&quot;80&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; letter-spacing=&quot;1&quot;&amp;gt;JITTERED VIEWS&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;35&quot; width=&quot;80&quot; height=&quot;50&quot; fill=&quot;#CFF0F6&quot; stroke=&quot;#0E7490&quot; stroke-width=&quot;1.5&quot; transform=&quot;rotate(-3 80 60)&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;95&quot; width=&quot;80&quot; height=&quot;50&quot; fill=&quot;#CFF0F6&quot; stroke=&quot;#0E7490&quot; stroke-width=&quot;1.5&quot; transform=&quot;rotate(2 80 120)&quot;/&amp;gt;
&amp;lt;rect x=&quot;40&quot; y=&quot;155&quot; width=&quot;80&quot; height=&quot;50&quot; fill=&quot;#CFF0F6&quot; stroke=&quot;#0E7490&quot; stroke-width=&quot;1.5&quot; transform=&quot;rotate(-1 80 180)&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g stroke=&quot;#8F8578&quot;&amp;gt;
&amp;lt;line x1=&quot;130&quot; y1=&quot;60&quot; x2=&quot;185&quot; y2=&quot;95&quot; marker-end=&quot;url(#a4)&quot;/&amp;gt;
&amp;lt;line x1=&quot;130&quot; y1=&quot;120&quot; x2=&quot;185&quot; y2=&quot;120&quot; marker-end=&quot;url(#a4)&quot;/&amp;gt;
&amp;lt;line x1=&quot;130&quot; y1=&quot;180&quot; x2=&quot;185&quot; y2=&quot;145&quot; marker-end=&quot;url(#a4)&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;defs&amp;gt;&amp;lt;marker id=&quot;a4&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;5&quot; markerHeight=&quot;5&quot; orient=&quot;auto&quot;&amp;gt;&amp;lt;path d=&quot;M0,0 L10,5 L0,10 z&quot; fill=&quot;#8F8578&quot;/&amp;gt;&amp;lt;/marker&amp;gt;&amp;lt;/defs&amp;gt;
&amp;lt;rect x=&quot;190&quot; y=&quot;95&quot; width=&quot;100&quot; height=&quot;50&quot; fill=&quot;none&quot; stroke=&quot;#0E7490&quot; stroke-width=&quot;1.5&quot;/&amp;gt;
&amp;lt;text x=&quot;240&quot; y=&quot;120&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;13&quot; font-weight=&quot;600&quot; fill=&quot;#0E7490&quot;&amp;gt;SigLIP2&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;240&quot; y=&quot;136&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot; font-style=&quot;italic&quot;&amp;gt;low-res encoder&amp;lt;/text&amp;gt;
&amp;lt;line x1=&quot;295&quot; y1=&quot;120&quot; x2=&quot;330&quot; y2=&quot;120&quot; stroke=&quot;#8F8578&quot; marker-end=&quot;url(#a4)&quot;/&amp;gt;
&amp;lt;g font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot;&amp;gt;&amp;lt;text x=&quot;395&quot; y=&quot;22&quot; text-anchor=&quot;middle&quot; letter-spacing=&quot;1&quot;&amp;gt;LOW-RES FEATURE MAPS&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;rect x=&quot;335&quot; y=&quot;35&quot; width=&quot;60&quot; height=&quot;50&quot; fill=&quot;#CFF0F6&quot; stroke=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;rect x=&quot;400&quot; y=&quot;35&quot; width=&quot;60&quot; height=&quot;50&quot; fill=&quot;#CFF0F6&quot; stroke=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;rect x=&quot;335&quot; y=&quot;95&quot; width=&quot;60&quot; height=&quot;50&quot; fill=&quot;#CFF0F6&quot; stroke=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;rect x=&quot;400&quot; y=&quot;95&quot; width=&quot;60&quot; height=&quot;50&quot; fill=&quot;#CFF0F6&quot; stroke=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;rect x=&quot;335&quot; y=&quot;155&quot; width=&quot;60&quot; height=&quot;50&quot; fill=&quot;#CFF0F6&quot; stroke=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;rect x=&quot;400&quot; y=&quot;155&quot; width=&quot;60&quot; height=&quot;50&quot; fill=&quot;#CFF0F6&quot; stroke=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;text x=&quot;397&quot; y=&quot;220&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot; font-style=&quot;italic&quot;&amp;gt;all jittered, misaligned&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;line x1=&quot;465&quot; y1=&quot;120&quot; x2=&quot;500&quot; y2=&quot;120&quot; stroke=&quot;#8F8578&quot; marker-end=&quot;url(#a4)&quot;/&amp;gt;
&amp;lt;text x=&quot;505&quot; y=&quot;110&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot; font-style=&quot;italic&quot;&amp;gt;solve:&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;505&quot; y=&quot;125&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot; font-style=&quot;italic&quot;&amp;gt;the HR map that,&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;505&quot; y=&quot;138&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot; font-style=&quot;italic&quot;&amp;gt;downsampled, explains&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;505&quot; y=&quot;151&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot; font-style=&quot;italic&quot;&amp;gt;every view&amp;lt;/text&amp;gt;
&amp;lt;rect x=&quot;580&quot; y=&quot;60&quot; width=&quot;80&quot; height=&quot;120&quot; fill=&quot;#FBE9CE&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;g stroke=&quot;#B8651A&quot; stroke-width=&quot;.5&quot; opacity=&quot;.4&quot;&amp;gt;
&amp;lt;line x1=&quot;580&quot; y1=&quot;90&quot; x2=&quot;660&quot; y2=&quot;90&quot;/&amp;gt;&amp;lt;line x1=&quot;580&quot; y1=&quot;120&quot; x2=&quot;660&quot; y2=&quot;120&quot;/&amp;gt;&amp;lt;line x1=&quot;580&quot; y1=&quot;150&quot; x2=&quot;660&quot; y2=&quot;150&quot;/&amp;gt;
&amp;lt;line x1=&quot;600&quot; y1=&quot;60&quot; x2=&quot;600&quot; y2=&quot;180&quot;/&amp;gt;&amp;lt;line x1=&quot;620&quot; y1=&quot;60&quot; x2=&quot;620&quot; y2=&quot;180&quot;/&amp;gt;&amp;lt;line x1=&quot;640&quot; y1=&quot;60&quot; x2=&quot;640&quot; y2=&quot;180&quot;/&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;620&quot; y=&quot;200&quot; text-anchor=&quot;middle&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;10&quot; fill=&quot;#B8651A&quot; letter-spacing=&quot;1&quot;&amp;gt;HIGH-RES MAP&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;620&quot; y=&quot;215&quot; text-anchor=&quot;middle&quot; font-family=&quot;serif&quot; font-size=&quot;10&quot; fill=&quot;#6B6258&quot; font-style=&quot;italic&quot;&amp;gt;consistent detail&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 9.&amp;lt;/strong&amp;gt; FeatSharp&apos;s multi-view consistency: many jittered versions of the same input produce many low-res feature maps, misaligned in different ways. The algorithm solves for a single high-res feature map that, under each jitter and downsampling, reproduces every view. Details consistent across views are real; details that flicker are noise.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;The result: a clean, high-resolution SigLIP2 feature map that is fully consistent with what SigLIP2 would have produced at low resolution, and contains only the details that survive the multi-view consistency test. No hallucinated boundaries. No plaid artifacts. A supervision signal C-RADIOv4 can actually learn from.&lt;/p&gt;
&lt;p&gt;FeatSharp is a small module, about as expensive as one extra transformer block, and it plugs into the C-RADIOv4 training pipeline as a preprocessor for SigLIP2&apos;s targets. It&apos;s also useful independently: any time you have a pretrained low-resolution vision encoder and want to extract richer spatial information from it, FeatSharp can upgrade the feature resolution without retraining the encoder.&lt;/p&gt;
&lt;h3&gt;15. Interlude: borrowing from biology&lt;/h3&gt;
&lt;p&gt;Before we put the whole pipeline together, one more idea deserves its own section. It&apos;s used across all three papers we&apos;ve discussed, but it&apos;s not really about computer vision. It&apos;s about how neural networks should be trained in the first place. And it&apos;s borrowed, more literally than is typical, from biology.&lt;/p&gt;
&lt;p&gt;Here&apos;s the setup. When you train a neural network, a common trick for improving robustness is to inject noise somewhere in the training process. Add a bit of Gaussian noise to the inputs. Add dropout to the activations. Add weight decay to the gradients. All of these are additive perturbations, and they all work reasonably well.&lt;/p&gt;
&lt;p&gt;Biological synapses work differently. Real synapses in real brains do something called &lt;em&gt;multiplicative noise&lt;/em&gt;. The fluctuation in a synaptic signal is proportional to the signal&apos;s magnitude. When a synapse is firing weakly, its noise is small. When it&apos;s firing strongly, its noise is big. The ratio stays roughly constant, a 10% jitter around whatever the signal is. This follows a &lt;em&gt;log-normal&lt;/em&gt; distribution, which has been measured experimentally in mouse brains, rat brains, human brains. It&apos;s a universal property of biological neural machinery.&lt;/p&gt;
&lt;p&gt;Another way to see the difference: imagine the volume knob on a stereo. If the music is playing quietly and you bump the knob slightly, you barely notice. If the music is already at maximum volume and you bump the knob by the same amount, the change is enormous and chaotic. Biological noise is the second kind. Additive noise in standard ML training is the first kind. They produce qualitatively different optimization dynamics.&lt;/p&gt;
&lt;p&gt;In 2024, a paper called &lt;em&gt;DAMP&lt;/em&gt; (Data Augmentation via Multiplicative Perturbations) made a clever observation: an input corruption, to first order, looks a lot like a multiplicative perturbation of the weights in the downstream layer. So instead of augmenting the data, you can augment the weights: multiply each weight by a small random Gaussian during the forward pass, and train normally. DAMP demonstrated that this is enough to produce networks that are dramatically more robust to real-world input corruptions. No extra data, no extra forward passes. Just change the noise structure.&lt;/p&gt;
&lt;p&gt;DAMP&apos;s justification is geometric, not biological. But the family of techniques it opens up turns out to line up closely with what biology does. In 2025, &lt;em&gt;LMD&lt;/em&gt; (Log-Normal Multiplicative Dynamics) made the biological motivation explicit: it derives a Bayesian learning rule that assumes log-normal posterior distributions over weights, directly inspired by the log-normal distribution of biological synaptic weights, and uses multiplicative updates with both noise and regularization applied multiplicatively. LMD achieves stable training at very low numerical precision, which matters a lot for next-generation hardware.&lt;/p&gt;
&lt;p&gt;C-RADIOv4&apos;s training recipe explicitly uses both DAMP and a related technique, &lt;em&gt;MESA&lt;/em&gt; (Memory-Efficient Sharpness-Aware Training), a cousin of these methods that approximates Sharpness-Aware Minimization via a trajectory loss against an EMA teacher, driving the optimizer into &quot;flat&quot; regions of the loss landscape, where small perturbations don&apos;t change the loss much. Flat regions generalize better than sharp regions. So you get the same dataset, the same architecture, and a model that degrades more gracefully on unseen data. Closely related in spirit: ASAM (Adaptive SAM) has been shown to be mathematically equivalent to optimizing under adversarial multiplicative weight perturbations, which is what ties the sharpness-aware line back to the multiplicative-noise story in this section.&lt;/p&gt;
&lt;p&gt;The bigger lesson: a lot of ML practice inherits assumptions from statistics textbooks (Gaussian noise, L2 penalties, isotropic distributions) that have nothing to do with how biological computation actually works. Occasionally, questioning those assumptions and reaching into neuroscience produces a free lunch. Multiplicative weight perturbation is one of the cleanest free lunches of the last few years.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There&apos;s a philosophical angle worth noting. We&apos;ve spent two decades assuming that biological intelligence and artificial intelligence are &quot;different kinds of systems,&quot; and that trying to directly translate neural biology into deep learning is a mug&apos;s game. Sometimes that&apos;s right. Backpropagation is not how brains learn, and anyone who says otherwise is selling something. But the log-normal synapse story is a counterexample. A specific, measured, boring property of real neurons (their noise is proportional to their signal) turns out to make artificial networks measurably better when imitated. Not every biological property will. Some of them will. The field is slowly learning which.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;16. Putting it together: the pipeline&lt;/h3&gt;
&lt;p&gt;Let&apos;s zoom back out. We&apos;ve now covered every piece. Here&apos;s the full training pipeline for a 2026 vision foundation model, from raw images to deployable checkpoint.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;
&amp;lt;svg viewBox=&quot;0 0 900 260&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; height=&quot;auto&quot;&amp;gt;
&amp;lt;g font-family=&quot;serif&quot; font-size=&quot;14&quot; font-weight=&quot;600&quot;&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;40&quot; width=&quot;200&quot; height=&quot;192&quot; fill=&quot;#F4EEE3&quot; stroke=&quot;#2563EB&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;rect x=&quot;30&quot; y=&quot;40&quot; width=&quot;200&quot; height=&quot;52&quot; fill=&quot;#2563EB&quot;/&amp;gt;
&amp;lt;text x=&quot;50&quot; y=&quot;58&quot; fill=&quot;#FFFFFF&quot; font-size=&quot;11&quot; letter-spacing=&quot;2&quot; font-family=&quot;JetBrains Mono&quot; font-weight=&quot;400&quot;&amp;gt;STAGE 1&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;130&quot; y=&quot;80&quot; text-anchor=&quot;middle&quot; fill=&quot;#FFFFFF&quot;&amp;gt;Pretrain&amp;lt;/text&amp;gt;
&amp;lt;g font-family=&quot;serif&quot; font-size=&quot;11.5&quot; fill=&quot;#1A1A1A&quot; font-weight=&quot;400&quot;&amp;gt;
&amp;lt;text x=&quot;45&quot; y=&quot;112&quot;&amp;gt;· Curated 1.7B images&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;45&quot; y=&quot;132&quot;&amp;gt;· DINO + iBOT losses&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;45&quot; y=&quot;152&quot;&amp;gt;· 1M iterations&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;45&quot; y=&quot;172&quot;&amp;gt;· ViT-7B, constant schedule&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;45&quot; y=&quot;207&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot; letter-spacing=&quot;1.5&quot;&amp;gt;RESULT&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;45&quot; y=&quot;222&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; fill=&quot;#2563EB&quot; font-style=&quot;italic&quot;&amp;gt;dense collapse at the end&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g stroke=&quot;#8F8578&quot; stroke-width=&quot;1.5&quot;&amp;gt;&amp;lt;line x1=&quot;233&quot; y1=&quot;136&quot; x2=&quot;252&quot; y2=&quot;136&quot; marker-end=&quot;url(#a5)&quot;/&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;defs&amp;gt;&amp;lt;marker id=&quot;a5&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;5&quot; markerHeight=&quot;5&quot; orient=&quot;auto&quot;&amp;gt;&amp;lt;path d=&quot;M0,0 L10,5 L0,10 z&quot; fill=&quot;#8F8578&quot;/&amp;gt;&amp;lt;/marker&amp;gt;&amp;lt;/defs&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;rect x=&quot;255&quot; y=&quot;40&quot; width=&quot;200&quot; height=&quot;192&quot; fill=&quot;#F4EEE3&quot; stroke=&quot;#15803D&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;rect x=&quot;255&quot; y=&quot;40&quot; width=&quot;200&quot; height=&quot;52&quot; fill=&quot;#15803D&quot;/&amp;gt;
&amp;lt;text x=&quot;275&quot; y=&quot;58&quot; fill=&quot;#FFFFFF&quot; font-size=&quot;11&quot; letter-spacing=&quot;2&quot; font-family=&quot;JetBrains Mono&quot;&amp;gt;STAGE 2&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;355&quot; y=&quot;80&quot; text-anchor=&quot;middle&quot; fill=&quot;#FFFFFF&quot; font-family=&quot;serif&quot; font-weight=&quot;600&quot; font-size=&quot;14&quot;&amp;gt;Gram Anchor&amp;lt;/text&amp;gt;
&amp;lt;g font-family=&quot;serif&quot; font-size=&quot;11.5&quot; fill=&quot;#1A1A1A&quot;&amp;gt;
&amp;lt;text x=&quot;270&quot; y=&quot;112&quot;&amp;gt;· Early-ckpt Gram teacher&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;270&quot; y=&quot;132&quot;&amp;gt;· Refresh every 10k iter&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;270&quot; y=&quot;152&quot;&amp;gt;· Add Gram loss&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;270&quot; y=&quot;172&quot;&amp;gt;· Refinement phase&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;270&quot; y=&quot;207&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot; letter-spacing=&quot;1.5&quot;&amp;gt;RESULT&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;270&quot; y=&quot;222&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; fill=&quot;#15803D&quot; font-style=&quot;italic&quot;&amp;gt;dense features restored&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g stroke=&quot;#8F8578&quot; stroke-width=&quot;1.5&quot;&amp;gt;&amp;lt;line x1=&quot;458&quot; y1=&quot;136&quot; x2=&quot;477&quot; y2=&quot;136&quot; marker-end=&quot;url(#a5)&quot;/&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;rect x=&quot;480&quot; y=&quot;40&quot; width=&quot;200&quot; height=&quot;192&quot; fill=&quot;#F4EEE3&quot; stroke=&quot;#0E7490&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;rect x=&quot;480&quot; y=&quot;40&quot; width=&quot;200&quot; height=&quot;52&quot; fill=&quot;#0E7490&quot;/&amp;gt;
&amp;lt;text x=&quot;500&quot; y=&quot;58&quot; fill=&quot;#FFFFFF&quot; font-size=&quot;11&quot; letter-spacing=&quot;2&quot; font-family=&quot;JetBrains Mono&quot;&amp;gt;STAGE 3&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;580&quot; y=&quot;80&quot; text-anchor=&quot;middle&quot; fill=&quot;#FFFFFF&quot; font-family=&quot;serif&quot; font-weight=&quot;600&quot; font-size=&quot;14&quot;&amp;gt;High-Res Adapt&amp;lt;/text&amp;gt;
&amp;lt;g font-family=&quot;serif&quot; font-size=&quot;11.5&quot; fill=&quot;#1A1A1A&quot;&amp;gt;
&amp;lt;text x=&quot;495&quot; y=&quot;112&quot;&amp;gt;· Mixed crops 512/768&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;495&quot; y=&quot;132&quot;&amp;gt;· Gram anchor still on&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;495&quot; y=&quot;152&quot;&amp;gt;· 10k iterations&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;495&quot; y=&quot;172&quot;&amp;gt;· Any-resolution support&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;495&quot; y=&quot;207&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot; letter-spacing=&quot;1.5&quot;&amp;gt;RESULT&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;495&quot; y=&quot;222&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; fill=&quot;#0E7490&quot; font-style=&quot;italic&quot;&amp;gt;stable up to 4k inference&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;g stroke=&quot;#8F8578&quot; stroke-width=&quot;1.5&quot;&amp;gt;&amp;lt;line x1=&quot;683&quot; y1=&quot;136&quot; x2=&quot;702&quot; y2=&quot;136&quot; marker-end=&quot;url(#a5)&quot;/&amp;gt;&amp;lt;/g&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;rect x=&quot;705&quot; y=&quot;40&quot; width=&quot;180&quot; height=&quot;192&quot; fill=&quot;#F4EEE3&quot; stroke=&quot;#B8651A&quot; stroke-width=&quot;2&quot;/&amp;gt;
&amp;lt;rect x=&quot;705&quot; y=&quot;40&quot; width=&quot;180&quot; height=&quot;52&quot; fill=&quot;#B8651A&quot;/&amp;gt;
&amp;lt;text x=&quot;725&quot; y=&quot;58&quot; fill=&quot;#FFFFFF&quot; font-size=&quot;11&quot; letter-spacing=&quot;2&quot; font-family=&quot;JetBrains Mono&quot;&amp;gt;STAGE 4&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;795&quot; y=&quot;80&quot; text-anchor=&quot;middle&quot; fill=&quot;#FFFFFF&quot; font-family=&quot;serif&quot; font-weight=&quot;600&quot; font-size=&quot;14&quot;&amp;gt;Agglomerate&amp;lt;/text&amp;gt;
&amp;lt;g font-family=&quot;serif&quot; font-size=&quot;11.5&quot; fill=&quot;#1A1A1A&quot;&amp;gt;
&amp;lt;text x=&quot;720&quot; y=&quot;112&quot;&amp;gt;· DINO + SigLIP + SAM&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;720&quot; y=&quot;132&quot;&amp;gt;· FeatSharp upsamples&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;720&quot; y=&quot;152&quot;&amp;gt;· PHI-S, shift eq, MESA&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;720&quot; y=&quot;172&quot;&amp;gt;· Stoch. res 128–1152&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;text x=&quot;720&quot; y=&quot;207&quot; font-family=&quot;JetBrains Mono&quot; font-size=&quot;9&quot; fill=&quot;#6B6258&quot; letter-spacing=&quot;1.5&quot;&amp;gt;RESULT&amp;lt;/text&amp;gt;
&amp;lt;text x=&quot;720&quot; y=&quot;222&quot; font-family=&quot;serif&quot; font-size=&quot;11&quot; fill=&quot;#B8651A&quot; font-style=&quot;italic&quot;&amp;gt;unified 631M student&amp;lt;/text&amp;gt;
&amp;lt;/g&amp;gt;
&amp;lt;/svg&amp;gt;
&amp;lt;figcaption&amp;gt;&amp;lt;strong&amp;gt;Fig 10.&amp;lt;/strong&amp;gt; The four-stage pipeline. Each stage fixes a specific problem introduced by the previous one. Stage 1 trains a ViT-7B on curated data and exposes dense feature collapse. Stage 2 fixes collapse via Gram anchoring. Stage 3 extends the model to arbitrary resolutions. Stage 4 distills it, alongside SigLIP2 and SAM3, into a smaller, more efficient student that carries all three specialists&apos; capabilities.&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;There&apos;s one more piece worth explaining: how the pipeline scales &lt;em&gt;down&lt;/em&gt; for deployment. The 6.7-billion-parameter DINOv3-7B model is wonderful for research, terrible for production. It&apos;s slow, expensive, and hard to serve. Most real use cases want a model that fits on a single GPU, runs in under 100ms, and doesn&apos;t require a dedicated inference cluster.&lt;/p&gt;
&lt;p&gt;The solution is &lt;em&gt;multi-student distillation&lt;/em&gt;. Instead of training one small student to mimic the big teacher, you train an entire family of them simultaneously: a ViT-Small (21M params), a ViT-Base (86M), a ViT-Large (304M), a few ConvNeXt variants, all at once. The real trick: you only run the expensive teacher once per batch. Cache its outputs. Then broadcast those cached targets to every student. Each student consumes the same distillation signal but learns its own efficient architecture.&lt;/p&gt;
&lt;p&gt;This is how a 6.7B self-supervised teacher ends up producing a 21M ConvNeXt you can put on a Jetson. It&apos;s also why the DINOv3 &quot;model family&quot; includes so many sizes: Meta trained them all in parallel, sharing the teacher&apos;s compute across every student.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Checkpoint: what you know now.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The full pipeline: pretrain a big ViT with self-supervision → repair dense features with Gram anchoring → adapt to arbitrary resolutions → distill multiple specialist teachers into a unified student → distill the student into many smaller students for deployment.&lt;/p&gt;
&lt;p&gt;The output: a family of vision backbones ranging from 21M to 840M parameters (and a 6.7B research flagship on the DINOv3 side) that all share the same fundamental capabilities (classification, segmentation, depth estimation, text alignment, video tracking, cross-domain transfer) in a single forward pass. You pick the size that fits your hardware budget.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;17. Three ideas worth stealing&lt;/h3&gt;
&lt;p&gt;We&apos;ve covered a lot of ground: self-supervision, Gram matrices, multi-teacher distillation, angular dispersion, NeRF-inspired upsampling, log-normal synapses. If you only take three things away from all of it, I would pick these:&lt;/p&gt;
&lt;h4&gt;1. Regularize relationships, not values.&lt;/h4&gt;
&lt;p&gt;Gram anchoring is the cleanest example of a pattern that keeps showing up across machine learning: when some property of your model is degrading during training, the fix is often not to freeze the &lt;em&gt;values&lt;/em&gt; of the relevant quantities but to freeze their &lt;em&gt;relational structure&lt;/em&gt;. The Gram matrix preserves pairwise similarities while letting individual features evolve. The underlying idea, pin the geometry and free the content, is applicable far outside vision. Any time you have a representation that you want to stay structurally coherent while continuing to learn new things, ask what relational property captures the structure you care about, and regularize that.&lt;/p&gt;
&lt;h4&gt;2. Heterogeneous signals need geometric normalization.&lt;/h4&gt;
&lt;p&gt;The C-RADIOv4 three-teacher problem is, at its heart, a generic issue with combining evidence from sources with different natural scales. Naive weighted averaging fails silently when the sources have different variances or different angular ranges. You don&apos;t get a helpful error message; you get a model that ignored the quieter source. The fix is to normalize by each source&apos;s intrinsic scale before combining. PHI-S is one specific way to do this in feature space, but the general principle (measure each source&apos;s natural magnitude and divide it out before summing) applies broadly. Multi-loss optimization, multi-task learning, sensor fusion, ensemble methods: all of them have this failure mode, and the fix is always some variant of the same normalization.&lt;/p&gt;
&lt;h4&gt;3. Randomize what you don&apos;t want memorized.&lt;/h4&gt;
&lt;p&gt;Shift equivariance, stochastic multi-resolution, multiplicative weight perturbation: these are all, at some level, the same trick. If there&apos;s a structural property of your training setup that you don&apos;t want the model to encode as a shortcut, &lt;em&gt;randomize it during training&lt;/em&gt;. Fixed pixel coordinates become random offsets. Fixed resolutions become sampled resolutions. Fixed noise levels become proportional fluctuations. The model learns the part that&apos;s invariant to the randomization, which, by construction, is the part you actually want it to learn. This is an old idea dressed up in many clothes, but it bears repeating: if your model is learning something you didn&apos;t intend, look for a way to make that something &lt;em&gt;vary&lt;/em&gt; from batch to batch, and watch it disappear.&lt;/p&gt;
&lt;h2&gt;Epilogue&lt;/h2&gt;
&lt;p&gt;It&apos;s tempting to read the story of DINOv3 / C-RADIOv4 / FeatSharp as a procession of clever technical tricks, each patching over a specific failure mode. That&apos;s true, but it&apos;s not the whole truth. What&apos;s really happening, I think, is that the field is converging on a more mature understanding of what a vision model &lt;em&gt;is&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The early ViT models were trained on one objective and evaluated on one benchmark. They were good at that benchmark and bad at everything else. The 2025–2026 generation is trained on multiple objectives, evaluated on dozens of benchmarks across domains the model was never specialized for, and expected to do all of it from a single frozen backbone. The architecture didn&apos;t really change. What changed is the training recipe and, more importantly, the understanding of what &lt;em&gt;constraints&lt;/em&gt; the recipe needs to satisfy for the resulting features to be useful in the wild.&lt;/p&gt;
&lt;p&gt;Gram anchoring is a constraint. Shift equivariance is a constraint. Multi-resolution sampling is a constraint. Multiplicative noise is a constraint. Each one narrows the space of functions the model can learn, in exchange for making the functions that remain more robust, more transferable, more general. The field is learning to write down the right constraints.&lt;/p&gt;
&lt;p&gt;If there&apos;s a pattern here beyond computer vision, it&apos;s this: modern machine learning is increasingly about finding the right structure to preserve, not the right thing to learn from scratch. Gradient descent will learn almost anything, given enough data and compute. Making it learn the &lt;em&gt;right&lt;/em&gt; thing (the thing that transfers, the thing that generalizes, the thing that survives contact with reality) is a matter of writing down what must remain invariant. Dense feature structure must remain invariant to continued training. Cross-teacher gradients must remain balanced. Feature maps must remain invariant to small input shifts. Performance must remain invariant to resolution. These are not subtle philosophical demands. They are engineering constraints, with concrete losses and concrete training tricks that enforce them.&lt;/p&gt;
&lt;p&gt;The payoff is real. A single DINOv3 backbone beats specialized Earth-observation networks at satellite tree-height estimation. A single C-RADIOv4 backbone, at 631 million parameters, ties or beats its much larger peers (including DINOv3-H+, an 840M distilled sibling of its 7B teacher) on dense benchmarks. A single inference call provides classification, segmentation, depth, tracking, correspondence, text alignment, and open-vocabulary masking. The vision backbone is becoming, increasingly, &lt;em&gt;just&lt;/em&gt; a backbone: a single spinal cord that a thousand downstream heads can read from.&lt;/p&gt;
&lt;p&gt;Which is, when you think about it, pretty close to how biological vision works. A single visual cortex produces a representation that the rest of the brain interprets in a dozen different ways, for a dozen different purposes, using a dozen different downstream circuits. The representation is fixed; the interpretations are flexible. It took artificial vision a while to get there. It seems to be arriving.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;We used to think the blurry mess was a failure of the machine. It turns out the blur was a failure of the objective.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;DINOv3. Siméoni et al., Meta AI 2025 (&lt;a href=&quot;https://arxiv.org/abs/2508.10104&quot;&gt;arXiv:2508.10104&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;C-RADIOv4 Tech Report. Ranzinger et al., NVIDIA 2026 (&lt;a href=&quot;https://arxiv.org/abs/2601.17237&quot;&gt;arXiv:2601.17237&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;FeatSharp. Ranzinger et al., NVIDIA 2025 (&lt;a href=&quot;https://arxiv.org/abs/2502.16025&quot;&gt;arXiv:2502.16025&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;PHI-S (distribution balancing). Ranzinger et al., NVIDIA 2024 (&lt;a href=&quot;https://arxiv.org/abs/2410.01680&quot;&gt;arXiv:2410.01680&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;DAMP (multiplicative weight perturbations). Trinh et al. 2024 (&lt;a href=&quot;https://arxiv.org/abs/2406.16540&quot;&gt;arXiv:2406.16540&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;LMD (log-normal multiplicative dynamics). Nishida et al., RIKEN AIP 2025 (&lt;a href=&quot;https://arxiv.org/abs/2506.17768&quot;&gt;arXiv:2506.17768&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;SAM 3. Meta AI 2025 (&lt;a href=&quot;https://arxiv.org/abs/2511.16719&quot;&gt;arXiv:2511.16719&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;SigLIP 2. Tschannen et al., Google 2025.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Originally prepared for the Boon engineering team, April 2026.&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>I Ship 20 PRs a Weekend with Claude Code. Here&apos;s the Workflow.</title><link>https://www.augusteo.com/blog/claude-code-workflow-planning/</link><guid isPermaLink="true">https://www.augusteo.com/blog/claude-code-workflow-planning/</guid><description>Most people jump straight to coding with AI. That&apos;s the mistake. Here&apos;s my full single-instance workflow from planning to merge.</description><pubDate>Tue, 03 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I watched Claude Code try to delete three pages from our app because I told it to &quot;change the routing.&quot;&lt;/p&gt;
&lt;p&gt;That&apos;s not what I wanted. I wanted to redirect one button to a different page. But Claude read &quot;change the routing&quot; and decided the old pages weren&apos;t needed anymore. If I hadn&apos;t read the plan before hitting execute, it would&apos;ve nuked components we still needed elsewhere.&lt;/p&gt;
&lt;p&gt;This is the most important thing I can tell you about working with AI coding tools: &lt;strong&gt;the planning phase is everything.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;The Decision Tree&lt;/h2&gt;
&lt;p&gt;When a new task lands on your desk, the first question is simple: do you know exactly what to build?&lt;/p&gt;
&lt;p&gt;Sometimes the spec is crystal clear. A Linear ticket with screenshots, reproduction steps, the works. In that case, skip the brainstorming and go straight to Feature Dev. Tell Claude Code what you need, and it&apos;ll generate a plan.&lt;/p&gt;
&lt;p&gt;But most of the time? The spec is vague. A customer said something hand-wavy. Your PM wrote two sentences in a Slack thread. You kind of know what they want, but the details are fuzzy.&lt;/p&gt;
&lt;p&gt;That&apos;s when you brainstorm first.&lt;/p&gt;
&lt;h2&gt;Brainstorm Mode Goes Deep&lt;/h2&gt;
&lt;p&gt;When I use Superpower Brainstorm, Claude doesn&apos;t just ask &quot;what do you want to build?&quot; It goes deep. Should this icon be here? What happens when a user clicks this button? Should we use this component or that one? Do you want a modal or a new page?&lt;/p&gt;
&lt;p&gt;It&apos;s like a product discovery session, except it&apos;s just you and Claude, and it happens in five minutes instead of an hour-long meeting.&lt;/p&gt;
&lt;p&gt;People complain about &quot;AI slop&quot; in code. I get it. But slop is just under-specified spec. If you tell Claude &quot;build a login page&quot; with no other context, yeah, you&apos;ll get generic slop. It doesn&apos;t know your component library. It doesn&apos;t know your auth patterns. It doesn&apos;t know you already have a &lt;code&gt;FormInput&lt;/code&gt; component that handles validation.&lt;/p&gt;
&lt;p&gt;But if you brainstorm first, if you tell it &quot;reuse our existing FormInput, follow the pattern in the signup page, use our auth service not a new one,&quot; the output is clean. It matches your codebase. Because you told it exactly what &quot;good&quot; looks like in your project.&lt;/p&gt;
&lt;p&gt;The engineers on my team who get the best output are the ones who spend the most time on the spec. The ones who get slop are the ones who skip the brainstorm and type a one-liner.&lt;/p&gt;
&lt;p&gt;If your task involves any kind of UI, add Frontend Design to the brainstorm. It&apos;s surprisingly good at thinking through layouts and interactions before any code gets written.&lt;/p&gt;
&lt;h2&gt;Plan Mode: Shift+Tab Before Anything&lt;/h2&gt;
&lt;p&gt;Here&apos;s a tip that changed everything for me: always start in plan mode.&lt;/p&gt;
&lt;p&gt;Press Shift+Tab until you&apos;re in plan mode. Do all your brainstorming, feature definition, and design exploration here. Claude will think through the problem and show you a plan.&lt;/p&gt;
&lt;p&gt;Your job now is to read that plan.&lt;/p&gt;
&lt;p&gt;I can&apos;t stress this enough. Read the plan. This is the actual job now.&lt;/p&gt;
&lt;p&gt;Going back to my routing example: the plan said &quot;remove pages X, Y, and Z since routing will change.&quot; I caught it because I read the plan. I told Claude &quot;don&apos;t delete those pages, just change where the button points to.&quot; It updated the plan in seconds.&lt;/p&gt;
&lt;p&gt;Another common one: Claude will want to create a brand new component when you already have one that does basically the same thing. I&apos;ll see that in the plan and say &quot;reuse the existing DataTable component, don&apos;t create a new one. Follow the same pattern we use in the estimates view.&quot; Then it goes back into the codebase, finds the pattern, and adjusts the plan.&lt;/p&gt;
&lt;p&gt;Same thing with tests. If the plan says &quot;add unit tests&quot; but it&apos;s only covering the happy path, tell it: &quot;make sure the unit testing is comprehensive.&quot; It&apos;ll go deeper on edge cases, error handling, boundary conditions. The plan shapes how much effort Claude puts into each part of the execution. If you don&apos;t push back on thin test coverage during planning, you&apos;ll get thin test coverage in the code.&lt;/p&gt;
&lt;p&gt;This back-and-forth takes maybe two or three rounds. Sometimes five for complex features. It&apos;s the most valuable time you&apos;ll spend.&lt;/p&gt;
&lt;h2&gt;Research During Planning&lt;/h2&gt;
&lt;p&gt;Here&apos;s something most people miss: Claude&apos;s training data is already outdated by the time you&apos;re using it.&lt;/p&gt;
&lt;p&gt;Real example from my team: during a planning session, Claude proposed using &lt;code&gt;tailscale serve&lt;/code&gt; for exposing a local service. I knew that was the old way. So I told it: &quot;research the best practice for Tailscale in 2026.&quot; It did a web search, found that &lt;code&gt;tailscale services&lt;/code&gt; is the newer approach (lets you run multiple services on the same node), and updated the plan.&lt;/p&gt;
&lt;p&gt;Don&apos;t trust Claude&apos;s memory for anything that changes fast. Tell it to use Context7 for documentation lookups, or just say &quot;research the current best practice for X.&quot; It combines web search with doc lookups and comes back with something current.&lt;/p&gt;
&lt;p&gt;This is especially important for frameworks and libraries. React patterns from 2023 are not React patterns from 2026. Always verify.&lt;/p&gt;
&lt;h2&gt;Feed It Everything You&apos;ve Got&lt;/h2&gt;
&lt;p&gt;The more context you give Claude during planning, the better your plan comes out.&lt;/p&gt;
&lt;p&gt;We use the Linear MCP, so I just paste the ticket URL into Claude Code. It fetches everything (description, comments, acceptance criteria) automatically. I don&apos;t even need to describe the task most of the time.&lt;/p&gt;
&lt;p&gt;Here&apos;s what that looks like in practice. I run &lt;code&gt;/feature-dev&lt;/code&gt; with a Linear URL, and Claude fetches the ticket, spins up parallel exploration agents to understand the codebase, and builds a multi-phase plan:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/claude-code-workflow-planning/feature-dev-linear.png&quot; alt=&quot;feature-dev-linear.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If someone reports a bug with a video, I&apos;ll watch the video myself, take screenshots of the key moments (what&apos;s wrong, what it should look like), and drag them straight into the terminal. Claude handles images surprisingly well for UI problems. We even built an &lt;code&gt;analyze-video&lt;/code&gt; skill that automatically extracts screenshots from videos for Claude. Happy to share if anyone wants it.[^1]&lt;/p&gt;
&lt;p&gt;I also pull context from other repos. &quot;Hey, look at the backend repo at ~/projects/boon-api and find how we handle PDF processing there.&quot; Claude reads the relevant files and incorporates that context into the plan. I keep my repos in sibling folders so they can reference each other easily.&lt;/p&gt;
&lt;p&gt;If your product docs live in Notion or Google Docs, use those MCPs too. Pass in the PRD link and Claude pulls the context it needs.&lt;/p&gt;
&lt;h2&gt;Execute: Clear Context and Let It Run&lt;/h2&gt;
&lt;p&gt;Once my plan looks good, I execute it. And here&apos;s the counterintuitive part: I clear the context 90% of the time.&lt;/p&gt;
&lt;p&gt;The plan itself contains everything Claude needs to execute. All that back-and-forth conversation about &quot;should we use this or that?&quot; is noise at execution time. Clear it. Start fresh with just the plan.&lt;/p&gt;
&lt;p&gt;The 10% where I keep context: when there was a really specific conversation about an edge case or architectural decision that isn&apos;t captured in the plan. But that&apos;s rare.&lt;/p&gt;
&lt;h3&gt;On &lt;code&gt;--dangerously-skip-permissions&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Yeah, I use it. I&apos;ve been using it since day one.&lt;/p&gt;
&lt;p&gt;I know this scares people. You read horror stories online about AI agents deleting databases or nuking folders. But here&apos;s the thing: if you did the planning phase right, Claude already showed you exactly what it&apos;s going to do. You reviewed the plan. You know what files it&apos;ll touch.&lt;/p&gt;
&lt;p&gt;The worst thing that ever happened to me: it merged directly to main instead of opening a PR. I reverted it in 30 seconds. That&apos;s it.&lt;/p&gt;
&lt;p&gt;If you&apos;re uncomfortable with it, don&apos;t use it. But understand that every permission prompt interrupts your flow. That adds up fast, especially when you&apos;re running multiple instances (more on that in Part 2).&lt;/p&gt;
&lt;h2&gt;The PR Pipeline&lt;/h2&gt;
&lt;p&gt;OK so Claude finishes a task. It&apos;s run the linter, run the tests, everything passes. Now what?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 1: Superpower Code Review.&lt;/strong&gt; Before pushing anything, I tell Claude to run the Superpower Code Review skill on its own work. Think of it as the &quot;are you sure about this?&quot; pass. It catches obvious mistakes, inconsistencies, things that don&apos;t match the codebase patterns.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 2: Push and open the PR.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 3: Automated PR review.&lt;/strong&gt; At Boon, we have the Claude Code PR Review toolkit running alongside Cubic Reviewer on every PR. These catch stuff that the Superpower review missed. Different perspective, different checks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 4: Resolve PR comments.&lt;/strong&gt; This is where the &lt;code&gt;pr-feedback&lt;/code&gt; plugin earns its keep.[^1] I go back to the same Claude Code instance (still has all the context from planning and execution) and say:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Please look at the comments on the PR and then resolve them if needed and make sure to push the changes.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I do this on every single PR. Every one. Claude loads the worktree and PR feedback skills automatically, then fetches the PR comments:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/claude-code-workflow-planning/pr-resolve-skills.png&quot; alt=&quot;pr-resolve-skills.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Then it does something really smart. It makes a structured assessment table of every review comment, triaging each one: valid (will fix), already resolved, out of scope (follow-up PR), or pre-existing (not introduced by this PR). Then it implements only the real fixes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/claude-code-workflow-planning/pr-resolve-assessment.png&quot; alt=&quot;pr-resolve-assessment.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It resolves every comment thread so the CI gates pass.&lt;/p&gt;
&lt;p&gt;This works really well because Claude still has the full context from the planning and execution stages. It knows &lt;em&gt;why&lt;/em&gt; it made each decision, so it can have an intelligent conversation with the reviewer about whether a comment is valid.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 5: Human review.&lt;/strong&gt; By the time a human reviewer sees this PR, it&apos;s been through: your plan review, Superpower Code Review, automated PR toolkit review, Cubic review, and Claude&apos;s comment resolution pass. The human is looking at a clean, well-tested PR. Their job is to catch architectural concerns and business logic issues, not formatting bugs or missing null checks.&lt;/p&gt;
&lt;h2&gt;Tips&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Working on multiple tickets?&lt;/strong&gt; Use git worktrees so each Claude Code session has its own branch and working directory:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# I use atuin for cross-device alias sync, but a plain alias works fine
alias ccw=&apos;claude --dangerously-skip-permissions --worktree&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Type &lt;code&gt;ccw&lt;/code&gt; and you get a new session with its own worktree. Three keystrokes. I&apos;ll cover the full parallel workflow in Part 2.&lt;/p&gt;
&lt;h2&gt;The Full Flow&lt;/h2&gt;
&lt;p&gt;Here&apos;s the complete single-instance workflow:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;                    ┌─────────────┐
                    │  New Task   │
                    └──────┬──────┘
                           │
                    ┌──────▼──────┐
                    │ Spec clear? │
                    └──┬───────┬──┘
                   Yes │       │ No
                       │       │
              ┌────────▼┐  ┌──▼──────────────────────┐
              │ Feature │  │ Superpower Brainstorm    │
              │   Def   │  │ (+ Frontend Design if UI)│
              └────┬────┘  └────────────┬─────────────┘
                   │                    │
                   └────────┬───────────┘
                            │
                   ┌────────▼─────────┐
                   │    Plan Mode     │
                   │  (Shift + Tab)   │
                   └────────┬─────────┘
                            │
                   ┌────────▼─────────┐
                   │   Review Plan    │◄──────────┐
                   │  (READ IT!)      │           │
                   └────────┬─────────┘           │
                            │                     │
                   ┌────────▼─────────┐    ┌──────┴──────┐
                   │   Plan good?     │ No │ Iterate in  │
                   └───┬──────────┬───┘───►│  English    │
                   Yes │          └────────└─────────────┘
                       │
              ┌────────▼──────────┐
              │ Execute           │
              │ (clear context)   │
              └────────┬──────────┘
                       │
                  Claude codes...
                       │
              ┌────────▼──────────┐
              │ Superpower        │
              │ Code Review       │──► auto-fixes findings
              └────────┬──────────┘
                       │
              ┌────────▼──────────┐
              │ Manual Testing    │
              │ (you test it)     │
              └────────┬──────────┘
                       │
              ┌────────▼──────────┐
              │ Push &amp;amp; Open PR    │
              └────────┬──────────┘
                       │
              ┌────────▼──────────┐
              │ CI +              |
              | Automated Review  │
              │ (Cubic + PR       │
              │  Review Toolkit)  │
              └────────┬──────────┘
                       │
              ┌────────▼──────────┐
              │  /pr-feedback     │
              │  (resolve or      │
              │   recommend)      │──► shows you what to address
              └────────┬──────────┘
                       │
              ┌────────▼──────────┐
              │  Human Decision   │
              │  on comments      │──► &quot;all of them&quot; keeps going
              └────────┬──────────┘
                       │
              ┌────────▼──────────┐
              │  Human Review     │
              └────────┬──────────┘
                       │
              ┌────────▼──────────┐
              │     Merge ✓       │
              └───────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything above is one Claude Code instance. One task, start to finish. Plan, execute, review, push, merge.&lt;/p&gt;
&lt;p&gt;But I don&apos;t work one at a time. I run five of these in parallel. In one weekend, I shipped 20 PRs.&lt;/p&gt;
&lt;p&gt;Part 2 is about how.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is Part 1 of a 3-part series on my Claude Code workflow. &lt;a href=&quot;/blog/claude-code-workflow-parallel&quot;&gt;Part 2: Parallel Execution&lt;/a&gt; drops tomorrow.&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;[^1]: &lt;strong&gt;Company Plugin Store.&lt;/strong&gt; Claude Code lets you add custom marketplaces alongside the official one. We set up an internal repo and anyone on the team can install it with one command: &lt;code&gt;/plugin marketplace add getboon/boon-plugins&lt;/code&gt;. Once added, it shows up right next to the official Anthropic marketplace:
&lt;img src=&quot;https://www.augusteo.com/assets/blog/claude-code-workflow-planning/boon-plugins-marketplace.png&quot; alt=&quot;boon-plugins-marketplace.png&quot; /&gt;
From there, engineers browse and install company-specific plugins. We&apos;ve got plugins for Linear, Sentry, Slack, video analysis (&lt;code&gt;analyze-video&lt;/code&gt;), and &lt;code&gt;pr-feedback&lt;/code&gt;.
&lt;img src=&quot;https://www.augusteo.com/assets/blog/claude-code-workflow-planning/boon-plugins-discover.png&quot; alt=&quot;boon-plugins-discover.png&quot; /&gt;
Enable auto-update so everyone gets the latest plugins automatically.
&lt;img src=&quot;https://www.augusteo.com/assets/blog/claude-code-workflow-planning/boon-plugins-autoupdate.png&quot; alt=&quot;boon-plugins-autoupdate.png&quot; /&gt;
That&apos;s the beauty of a shared marketplace: when we add a new plugin or update an existing one, everyone gets it. No Slack messages saying &quot;hey update your config.&quot; If you want to set up something similar for your team, the &lt;a href=&quot;https://github.com/getboon/boon-plugins&quot;&gt;boon-plugins repo&lt;/a&gt; is a good reference.&lt;/p&gt;
</content:encoded></item><item><title>My Claude Code Plugin Stack After Months of Trial and Error</title><link>https://www.augusteo.com/blog/claude-code-plugin-stack/</link><guid isPermaLink="true">https://www.augusteo.com/blog/claude-code-plugin-stack/</guid><description>My full plugin stack after months of trial and error, why I only install from the official store, and what I tried and dropped.</description><pubDate>Mon, 09 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I use Claude Code every day. Here&apos;s my full plugin stack, nothing hidden.&lt;/p&gt;
&lt;p&gt;I only install from the &lt;a href=&quot;https://github.com/anthropics/claude-plugins-official&quot;&gt;official Anthropic plugin store&lt;/a&gt;. No community marketplaces. No random GitHub repos. It took months of trial and error to get here.&lt;/p&gt;
&lt;h2&gt;Why Official Only&lt;/h2&gt;
&lt;p&gt;Two reasons: simplicity and security.&lt;/p&gt;
&lt;p&gt;I don&apos;t think you need 20 fancy plugins to ship working software. The official store has enough to cover my entire workflow. That&apos;s it. That&apos;s the simplicity argument.&lt;/p&gt;
&lt;p&gt;The security argument is scarier.&lt;/p&gt;
&lt;p&gt;Snyk published their &lt;a href=&quot;https://snyk.io/blog/toxicskills-malicious-ai-agent-skills-clawhub/&quot;&gt;ToxicSkills study&lt;/a&gt; in early 2026. They scanned 3,984 skills from community marketplaces. The findings: &lt;strong&gt;13.4% contain critical security issues&lt;/strong&gt; (malware, prompt injection, exposed secrets). Over a third of the ecosystem (36.82%) has at least one flaw. They found 1,467 malicious payloads total.&lt;/p&gt;
&lt;p&gt;The barrier to publish a skill on community marketplaces? A markdown file and a GitHub account that&apos;s one week old. No code signing. No security review. No sandbox by default.&lt;/p&gt;
&lt;p&gt;PromptArmor took it further and &lt;a href=&quot;https://www.promptarmor.com/resources/hijacking-claude-code-via-injected-marketplace-plugins&quot;&gt;demonstrated how attackers can hijack Claude Code&lt;/a&gt; through injected marketplace plugins. The attack uses prompt injection to make Claude run curl commands that exfiltrate your codebase. Malicious hooks can bypass the human approval step entirely.&lt;/p&gt;
&lt;p&gt;SentinelOne showed that &lt;a href=&quot;https://www.sentinelone.com/blog/marketplace-skills-and-dependency-hijack-in-claude-code/&quot;&gt;marketplace skills can force Claude to install malicious dependencies&lt;/a&gt; without you knowing.&lt;/p&gt;
&lt;p&gt;The official store is curated by Anthropic. Third-party plugins go through a review process before they&apos;re accepted. That&apos;s the trust baseline I&apos;m comfortable with.&lt;/p&gt;
&lt;h2&gt;The Stack&lt;/h2&gt;
&lt;p&gt;Seven plugins and one MCP server. Here&apos;s what each one does and why it&apos;s in my stack.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/obra/superpowers&quot;&gt;Superpowers&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is the backbone. Created by &lt;a href=&quot;https://blog.fsck.com/2025/10/09/superpowers/&quot;&gt;Jesse Vincent&lt;/a&gt;, it&apos;s a skills framework that teaches Claude to actually think before it codes.&lt;/p&gt;
&lt;p&gt;The skills I use most:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Brainstorming&lt;/strong&gt; asks a ton of questions before you write any code. Depending on the task, it&apos;ll dig into requirements, edge cases, and alternatives you haven&apos;t considered.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Planning&lt;/strong&gt; creates structured implementation plans that you review before execution. No surprises.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code review&lt;/strong&gt; catches issues before they ship. I pair this with the security-guidance plugin (more on that below).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skill creation&lt;/strong&gt; helps you write new skills for Claude Code. I&apos;ve used it to build custom workflows for my blog writing process.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The whole philosophy is: stop and think before you code. It&apos;s the opposite of just letting Claude rip through your codebase unsupervised.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/upstash/context7&quot;&gt;Context7&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This one solves a real problem. LLMs hallucinate APIs. They suggest functions that don&apos;t exist, use deprecated patterns, or mix up versions. Context7 pulls up-to-date documentation straight from source repos and injects it into your prompts.&lt;/p&gt;
&lt;p&gt;I use it constantly. Astro when building my blog, Bun, uv, Remix, Rails. Without it, Claude will confidently suggest APIs that were deprecated two versions ago. Context7 makes sure Claude has the right docs.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://llmstxt.org/&quot;&gt;llms.txt standard&lt;/a&gt; is getting better at this problem too. Over 844,000 websites have adopted it, and platforms like &lt;a href=&quot;https://www.mintlify.com/blog/simplifying-docs-with-llms-txt&quot;&gt;Mintlify&lt;/a&gt; now generate llms.txt automatically. But adoption isn&apos;t universal yet. Not every library has jumped on board. Context7 fills that gap.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/anthropics/claude-code/tree/main/plugins/code-review&quot;&gt;Code Review&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I use this for reviewing &lt;strong&gt;my own work&lt;/strong&gt; before committing. It runs multiple review agents in parallel (checking for bugs, style, CLAUDE.md compliance) and gives confidence scores so you can focus on what actually matters.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/anthropics/claude-code/tree/main/plugins/pr-review-toolkit&quot;&gt;PR Review Toolkit&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I use this for reviewing &lt;strong&gt;other people&apos;s PRs&lt;/strong&gt; on GitHub. Six specialized agents that each focus on a different aspect: code quality, test coverage, error handling, type design, comments, and code simplification.&lt;/p&gt;
&lt;p&gt;The split is simple. Code Review for my code. PR Review Toolkit for everyone else&apos;s.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/anthropics/claude-code/tree/main/plugins/feature-dev&quot;&gt;Feature Dev&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Guided feature development with a 7-phase workflow: discovery, exploration, questions, architecture, implementation, review, summary. Sounds like a lot, but it forces Claude to understand the existing codebase before writing a single line.&lt;/p&gt;
&lt;p&gt;I don&apos;t use this for every feature. Small changes don&apos;t need this much structure. But for anything non-trivial, it keeps things organized.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/anthropics/claude-code/tree/main/plugins/frontend-design&quot;&gt;Frontend Design&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This one surprised me. I expected it to generate generic-looking UI. Instead, it &lt;a href=&quot;https://claude.com/blog/improving-frontend-design-through-skills&quot;&gt;reads your existing design system&lt;/a&gt; (your CSS, tokens, component patterns) and designs new UI that actually fits. It explicitly avoids the &quot;AI slop aesthetic&quot; (overused Inter font, purple gradients, cookie-cutter components).&lt;/p&gt;
&lt;p&gt;I use it even when I already have a bunch of existing UI. It respects what&apos;s there and adjusts accordingly.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/anthropics/claude-code/tree/main/plugins/security-guidance&quot;&gt;Security Guidance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Runs as a pre-tool hook. When Claude tries to write code with security issues (command injection, XSS, unsafe input handling), it blocks the edit and explains what&apos;s wrong. You fix it on the next turn.&lt;/p&gt;
&lt;p&gt;It pairs really well with superpowers&apos; code reviewer. Together they catch both security vulnerabilities and code quality issues.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://code.claude.com/docs/en/chrome&quot;&gt;Claude in Chrome&lt;/a&gt; (MCP)&lt;/h3&gt;
&lt;p&gt;This is the one that feels like magic. Claude controls a real Chrome browser to test your frontend.&lt;/p&gt;
&lt;p&gt;Here&apos;s how I use it: I change some backend logic in a Rails app that affects the frontend. Claude launches Chrome, navigates to the page, and verifies the UI looks right. I keep working on something else.&lt;/p&gt;
&lt;p&gt;It&apos;s not about catching extra bugs. It&apos;s about automation. For backend developers who touch frontend, this is a huge time saver.&lt;/p&gt;
&lt;h2&gt;What I Tried and Dropped&lt;/h2&gt;
&lt;p&gt;I&apos;ve tried pretty much everything in the official store. LSP plugins, Stripe, code-simplifier, and plenty of others. Most didn&apos;t make the cut. Here are the notable ones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Serena.&lt;/strong&gt; Opens a browser window every single time you start Claude Code. There are &lt;a href=&quot;https://github.com/oraios/serena/issues/613&quot;&gt;multiple&lt;/a&gt; &lt;a href=&quot;https://github.com/oraios/serena/discussions/271&quot;&gt;GitHub&lt;/a&gt; &lt;a href=&quot;https://github.com/oraios/serena/discussions/357&quot;&gt;issues&lt;/a&gt; with people complaining about this. If you&apos;re building a developer tool, don&apos;t break the developer&apos;s flow on startup. That&apos;s DX 101.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Playwright.&lt;/strong&gt; Redundant once I had Claude in Chrome. Same job, one less dependency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://claude.com/plugins/ralph-loop&quot;&gt;Ralph Loop&lt;/a&gt;.&lt;/strong&gt; This is the autonomous loop plugin. You give Claude a prompt and it runs in a loop until it declares &quot;DONE.&quot; No human in the loop. I&apos;ve &lt;a href=&quot;/blog/claude-ignored-my-instructions&quot;&gt;written before&lt;/a&gt; about why I don&apos;t trust fully autonomous workflows. I want to manage the plan. I want to review the diffs. Ralph is for people who are comfortable letting Claude run wild. I&apos;m not there yet.&lt;/p&gt;
&lt;h2&gt;What&apos;s Next&lt;/h2&gt;
&lt;p&gt;This post is just the what. I&apos;ll write a follow-up on the how (my actual daily workflow with these plugins). But if you&apos;re getting started with Claude Code plugins, this is the stack I&apos;d recommend.&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;/plugin&lt;/code&gt; in Claude Code. Go to the Discover tab. Start with superpowers and context7. Add the rest as you need them.&lt;/p&gt;
&lt;p&gt;And stick to the official store. Your credentials will thank you.&lt;/p&gt;
</content:encoded></item><item><title>The Claude Code Plugins I Use Every Day</title><link>https://www.augusteo.com/blog/claude-code-plugins-i-use-every-day/</link><guid isPermaLink="true">https://www.augusteo.com/blog/claude-code-plugins-i-use-every-day/</guid><description>My full plugin stack after months of trial and error, why I only install from the official store, and what I tried and dropped.</description><pubDate>Mon, 09 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I use Claude Code every day. Here&apos;s my full plugin stack, nothing hidden.&lt;/p&gt;
&lt;p&gt;I only install from the &lt;a href=&quot;https://github.com/anthropics/claude-plugins-official&quot;&gt;official Anthropic plugin store&lt;/a&gt;. No community marketplaces. No random GitHub repos. It took months of trial and error to get here.&lt;/p&gt;
&lt;h2&gt;Why Official Only&lt;/h2&gt;
&lt;p&gt;Two reasons: simplicity and security.&lt;/p&gt;
&lt;p&gt;I don&apos;t think you need 20 fancy plugins to ship working software. The official store has enough to cover my entire workflow. That&apos;s it. That&apos;s the simplicity argument.&lt;/p&gt;
&lt;p&gt;The security argument is scarier.&lt;/p&gt;
&lt;p&gt;Snyk published their &lt;a href=&quot;https://snyk.io/blog/toxicskills-malicious-ai-agent-skills-clawhub/&quot;&gt;ToxicSkills study&lt;/a&gt; in early 2026. They scanned 3,984 skills from community marketplaces. The findings: &lt;strong&gt;13.4% contain critical security issues&lt;/strong&gt; (malware, prompt injection, exposed secrets). Over a third of the ecosystem (36.82%) has at least one flaw. They found 1,467 malicious payloads total.&lt;/p&gt;
&lt;p&gt;The barrier to publish a skill on community marketplaces? A markdown file and a GitHub account that&apos;s one week old. No code signing. No security review. No sandbox by default.&lt;/p&gt;
&lt;p&gt;PromptArmor took it further and &lt;a href=&quot;https://www.promptarmor.com/resources/hijacking-claude-code-via-injected-marketplace-plugins&quot;&gt;demonstrated how attackers can hijack Claude Code&lt;/a&gt; through injected marketplace plugins. The attack uses prompt injection to make Claude run curl commands that exfiltrate your codebase. Malicious hooks can bypass the human approval step entirely.&lt;/p&gt;
&lt;p&gt;SentinelOne showed that &lt;a href=&quot;https://www.sentinelone.com/blog/marketplace-skills-and-dependency-hijack-in-claude-code/&quot;&gt;marketplace skills can force Claude to install malicious dependencies&lt;/a&gt; without you knowing.&lt;/p&gt;
&lt;p&gt;The official store is curated by Anthropic. Third-party plugins go through a review process before they&apos;re accepted. That&apos;s the trust baseline I&apos;m comfortable with.&lt;/p&gt;
&lt;h2&gt;The Stack&lt;/h2&gt;
&lt;p&gt;Seven plugins and one MCP server. Here&apos;s what each one does and why it&apos;s in my stack.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/obra/superpowers&quot;&gt;Superpowers&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is the backbone. Created by &lt;a href=&quot;https://blog.fsck.com/2025/10/09/superpowers/&quot;&gt;Jesse Vincent&lt;/a&gt;, it&apos;s a skills framework that teaches Claude to actually think before it codes.&lt;/p&gt;
&lt;p&gt;The skills I use most:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Brainstorming&lt;/strong&gt; asks a ton of questions before you write any code. Depending on the task, it&apos;ll dig into requirements, edge cases, and alternatives you haven&apos;t considered.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Planning&lt;/strong&gt; creates structured implementation plans that you review before execution. No surprises.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code review&lt;/strong&gt; catches issues before they ship. I pair this with the security-guidance plugin (more on that below).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skill creation&lt;/strong&gt; helps you write new skills for Claude Code. I&apos;ve used it to build custom workflows for my blog writing process.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The whole philosophy is: stop and think before you code. It&apos;s the opposite of just letting Claude rip through your codebase unsupervised.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/upstash/context7&quot;&gt;Context7&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This one solves a real problem. LLMs hallucinate APIs. They suggest functions that don&apos;t exist, use deprecated patterns, or mix up versions. Context7 pulls up-to-date documentation straight from source repos and injects it into your prompts.&lt;/p&gt;
&lt;p&gt;I use it constantly. Astro when building my blog, Bun, uv, Remix, Rails. Without it, Claude will confidently suggest APIs that were deprecated two versions ago. Context7 makes sure Claude has the right docs.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://llmstxt.org/&quot;&gt;llms.txt standard&lt;/a&gt; is getting better at this problem too. Over 844,000 websites have adopted it, and platforms like &lt;a href=&quot;https://www.mintlify.com/blog/simplifying-docs-with-llms-txt&quot;&gt;Mintlify&lt;/a&gt; now generate llms.txt automatically. But adoption isn&apos;t universal yet. Not every library has jumped on board. Context7 fills that gap.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/anthropics/claude-code/tree/main/plugins/code-review&quot;&gt;Code Review&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I use this for reviewing &lt;strong&gt;my own work&lt;/strong&gt; before committing. It runs multiple review agents in parallel (checking for bugs, style, CLAUDE.md compliance) and gives confidence scores so you can focus on what actually matters.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/anthropics/claude-code/tree/main/plugins/pr-review-toolkit&quot;&gt;PR Review Toolkit&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I use this for reviewing &lt;strong&gt;other people&apos;s PRs&lt;/strong&gt; on GitHub. Six specialized agents that each focus on a different aspect: code quality, test coverage, error handling, type design, comments, and code simplification.&lt;/p&gt;
&lt;p&gt;The split is simple. Code Review for my code. PR Review Toolkit for everyone else&apos;s.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/anthropics/claude-code/tree/main/plugins/feature-dev&quot;&gt;Feature Dev&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Guided feature development with a 7-phase workflow: discovery, exploration, questions, architecture, implementation, review, summary. Sounds like a lot, but it forces Claude to understand the existing codebase before writing a single line.&lt;/p&gt;
&lt;p&gt;I don&apos;t use this for every feature. Small changes don&apos;t need this much structure. But for anything non-trivial, it keeps things organized.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/anthropics/claude-code/tree/main/plugins/frontend-design&quot;&gt;Frontend Design&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This one surprised me. I expected it to generate generic-looking UI. Instead, it &lt;a href=&quot;https://claude.com/blog/improving-frontend-design-through-skills&quot;&gt;reads your existing design system&lt;/a&gt; (your CSS, tokens, component patterns) and designs new UI that actually fits. It explicitly avoids the &quot;AI slop aesthetic&quot; (overused Inter font, purple gradients, cookie-cutter components).&lt;/p&gt;
&lt;p&gt;I use it even when I already have a bunch of existing UI. It respects what&apos;s there and adjusts accordingly.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/anthropics/claude-code/tree/main/plugins/security-guidance&quot;&gt;Security Guidance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Runs as a pre-tool hook. When Claude tries to write code with security issues (command injection, XSS, unsafe input handling), it blocks the edit and explains what&apos;s wrong. You fix it on the next turn.&lt;/p&gt;
&lt;p&gt;It pairs really well with superpowers&apos; code reviewer. Together they catch both security vulnerabilities and code quality issues.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://code.claude.com/docs/en/chrome&quot;&gt;Claude in Chrome&lt;/a&gt; (MCP)&lt;/h3&gt;
&lt;p&gt;This is the one that feels like magic. Claude controls a real Chrome browser to test your frontend.&lt;/p&gt;
&lt;p&gt;Here&apos;s how I use it: I change some backend logic in a Rails app that affects the frontend. Claude launches Chrome, navigates to the page, and verifies the UI looks right. I keep working on something else.&lt;/p&gt;
&lt;p&gt;It&apos;s not about catching extra bugs. It&apos;s about automation. For backend developers who touch frontend, this is a huge time saver.&lt;/p&gt;
&lt;h2&gt;What I Tried and Dropped&lt;/h2&gt;
&lt;p&gt;I&apos;ve tried pretty much everything in the official store. LSP plugins, Stripe, code-simplifier, and plenty of others. Most didn&apos;t make the cut. Here are the notable ones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Serena.&lt;/strong&gt; Opens a browser window every single time you start Claude Code. There are &lt;a href=&quot;https://github.com/oraios/serena/issues/613&quot;&gt;multiple&lt;/a&gt; &lt;a href=&quot;https://github.com/oraios/serena/discussions/271&quot;&gt;GitHub&lt;/a&gt; &lt;a href=&quot;https://github.com/oraios/serena/discussions/357&quot;&gt;issues&lt;/a&gt; with people complaining about this. If you&apos;re building a developer tool, don&apos;t break the developer&apos;s flow on startup. That&apos;s DX 101.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Playwright.&lt;/strong&gt; Redundant once I had Claude in Chrome. Same job, one less dependency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://claude.com/plugins/ralph-loop&quot;&gt;Ralph Loop&lt;/a&gt;.&lt;/strong&gt; This is the autonomous loop plugin. You give Claude a prompt and it runs in a loop until it declares &quot;DONE.&quot; No human in the loop. I&apos;ve &lt;a href=&quot;/blog/claude-ignored-my-instructions&quot;&gt;written before&lt;/a&gt; about why I don&apos;t trust fully autonomous workflows. I want to manage the plan. I want to review the diffs. Ralph is for people who are comfortable letting Claude run wild. I&apos;m not there yet.&lt;/p&gt;
&lt;h2&gt;What&apos;s Next&lt;/h2&gt;
&lt;p&gt;This post is just the what. I&apos;ll write a follow-up on the how (my actual daily workflow with these plugins). But if you&apos;re getting started with Claude Code plugins, this is the stack I&apos;d recommend.&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;/plugin&lt;/code&gt; in Claude Code. Go to the Discover tab. Start with superpowers and context7. Add the rest as you need them.&lt;/p&gt;
&lt;p&gt;And stick to the official store. Your credentials will thank you.&lt;/p&gt;
</content:encoded></item><item><title>Hand Tools, Power Tools, and the AI Coding Debate</title><link>https://www.augusteo.com/blog/hand-tools-power-tools-ai-coding-debate/</link><guid isPermaLink="true">https://www.augusteo.com/blog/hand-tools-power-tools-ai-coding-debate/</guid><description>What my late friend Paul&apos;s hand-tool woodworking philosophy taught me about the AI coding debate</description><pubDate>Sun, 08 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My friend Paul kept his woodworking tools in his living room. Japanese hand saws, chisels, marking gauges.&lt;/p&gt;
&lt;p&gt;My workshop is a two-car garage. Table saw, miter saw, and a growing pile of battery-powered everything.&lt;/p&gt;
&lt;p&gt;We both worked at Apple. We bonded over woodworking and electric unicycles. But we built things in completely different ways.&lt;/p&gt;
&lt;h2&gt;The Dovetail Moment&lt;/h2&gt;
&lt;p&gt;One day Paul showed me his hand-cut castle joints and dovetail joints. He was smiling, proud. The joints were precise, beautiful, fitted together perfectly. You could tell someone spent hours on each one.&lt;/p&gt;
&lt;p&gt;Then I showed him what I&apos;d tried with my table saw. It wasn&apos;t even close.&lt;/p&gt;
&lt;p&gt;I learned that day that table saws aren&apos;t really suitable for dovetail joints. I should&apos;ve used a router. But that&apos;s the thing. My first instinct was always: how do I make this as efficient as possible? Paul&apos;s was: how do I make this beautiful?&lt;/p&gt;
&lt;h2&gt;Paul at Apple&lt;/h2&gt;
&lt;p&gt;When I joined Apple, I needed to learn internal systems fast. Paul was my go-to person.&lt;/p&gt;
&lt;p&gt;Walking from the Apple Park parking lot to my building took 25 to 30 minutes. Paul was already riding an electric unicycle. He helped me pick one out and lent me his knee pads while I learned.&lt;/p&gt;
&lt;p&gt;When we hit a bug we couldn&apos;t figure out together, I asked him: what do you do when you hit a roadblock like this?&lt;/p&gt;
&lt;p&gt;&quot;We just read the code for longer until something comes.&quot;&lt;/p&gt;
&lt;p&gt;That was Paul. No shortcuts. No rushing. Just patience and attention.&lt;/p&gt;
&lt;p&gt;He was a good person. He passed away in 2022 at 45. Way too young.&lt;/p&gt;
&lt;h2&gt;The Debate Paul Missed&lt;/h2&gt;
&lt;p&gt;Paul died before ChatGPT, before Claude, before any of the AI coding tools that now dominate my workflow. I&apos;ll never know which side he&apos;d have taken.&lt;/p&gt;
&lt;p&gt;Would he have been a hand-tool coder, reading the code for longer, savoring the process? Or would he have treated AI as just another tool in his collection? I genuinely don&apos;t know.&lt;/p&gt;
&lt;p&gt;But the debate he missed is happening everywhere right now. And it sounds a lot like hand tools versus power tools.&lt;/p&gt;
&lt;p&gt;Abhinav Omprakash &lt;a href=&quot;https://www.abhinavomprakash.com/posts/i-am-happier-writing-code-by-hand/&quot;&gt;recently wrote&lt;/a&gt; about trying Claude Code three times and deleting it each time. Same pattern: depression, lethargy, loss of purpose. Each time he deleted it, he rediscovered the joy of coding.&lt;/p&gt;
&lt;p&gt;His argument is that writing code is thinking. When you outsource that to an AI, you skip the internalization. You become a reviewer of code you don&apos;t deeply understand.&lt;/p&gt;
&lt;p&gt;I get it.&lt;/p&gt;
&lt;h2&gt;I Used to Be a Craftsman&lt;/h2&gt;
&lt;p&gt;In 2016, I gave a talk at Melbourne Mobile Meetup about Software Craftsmanship. I quoted Paul Graham: programmers as the last remaining craftsmen. I talked about masters and journeymen and deliberate practice. I believed it.&lt;/p&gt;
&lt;p&gt;Around the same time, I was working on an iOS project with ReactiveCocoa and RxSwift. I learned to chain publishers and subscribers, replacing dozens of delegate functions with elegant reactive code. It was genuinely empowering. I still remember that feeling of everything clicking into place.&lt;/p&gt;
&lt;p&gt;That&apos;s the hand-tool moment. When the craft itself is the reward.&lt;/p&gt;
&lt;p&gt;But I&apos;ve changed my mind.&lt;/p&gt;
&lt;h2&gt;Code Is a Liability&lt;/h2&gt;
&lt;p&gt;Every line you write is a line you have to maintain, debug, and eventually delete or rewrite. The less code, the better. What matters isn&apos;t how the code looks or how elegantly it works internally. What matters is whether it solves the user&apos;s problem and improves their life.&lt;/p&gt;
&lt;p&gt;If it doesn&apos;t, the beauty of the code is irrelevant.&lt;/p&gt;
&lt;p&gt;Recently, our customer support manager Stacy found a bug. When you edited a user&apos;s role in the admin panel and saved, it blanked out their password. A mistake in the form submission.&lt;/p&gt;
&lt;p&gt;I spawned a debugging agent on Slack. It traced the routes, checked the controller, found the issue. I spawned another agent to fix it. It created a PR. CI passed. Merged.&lt;/p&gt;
&lt;p&gt;I never opened VSCode. I never hunted through files trying to find which form handles that page. The old way would&apos;ve taken me significantly longer. This took minutes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://techcrunch.com/2025/03/06/a-quarter-of-startups-in-ycs-current-cohort-have-codebases-that-are-almost-entirely-ai-generated/&quot;&gt;25% of the latest Y Combinator batch&lt;/a&gt; has codebases that are 95% AI-generated. These aren&apos;t amateurs. These are highly technical founders who could write it all by hand but chose not to.&lt;/p&gt;
&lt;p&gt;That number is only going up.&lt;/p&gt;
&lt;p&gt;The craftsman coders who insist on hand-writing everything will have a harder time keeping up. Just like hand-tool woodworkers. They&apos;ll make beautiful things. They&apos;ll enjoy the process. But when speed matters, they&apos;ll be outpaced by AI-enabled engineers.&lt;/p&gt;
&lt;h2&gt;The Table Saw Lesson&lt;/h2&gt;
&lt;p&gt;Every woodworker measures twice and cuts once. That&apos;s the craft, not the tool.&lt;/p&gt;
&lt;p&gt;The difference is what happens after the planning.&lt;/p&gt;
&lt;p&gt;With hand tools, you feel the saw bite into the wood, adjust your angle, correct mid-cut. The material talks back to you. That&apos;s what Paul meant by &quot;read the code for longer until something comes.&quot; The process itself is the reward.&lt;/p&gt;
&lt;p&gt;With a table saw, the cut happens in a second. If you&apos;re on the wrong side of the line by 1/8th of an inch, the piece doesn&apos;t fit. You can&apos;t undo it. People lose fingers to table saws. The tool doesn&apos;t care about your intentions.&lt;/p&gt;
&lt;p&gt;Both require thinking. Both require skill. But hand tools give you something power tools don&apos;t: the craft experience of the work itself.&lt;/p&gt;
&lt;p&gt;AI coding is the same trade-off. I don&apos;t throw prompts and hope for the best. I plan the approach, review the architecture, then let the agent execute. The thinking is the same. But I&apos;ve traded the joy of writing every line for the speed of not having to.&lt;/p&gt;
&lt;h2&gt;Two Kinds of Joy&lt;/h2&gt;
&lt;p&gt;Here&apos;s why I made that trade.&lt;/p&gt;
&lt;p&gt;The nightstand I built years ago still sits next to my bed. The wardrobe shelves I made hold my wife&apos;s clothes in our bedroom and my kid&apos;s toys in their room.&lt;/p&gt;
&lt;p&gt;I don&apos;t remember what it felt like to cut those pieces. The joy of building them is long gone. But I walk past them every day, still working, still useful. That joy is still here.&lt;/p&gt;
&lt;p&gt;There&apos;s the joy of making something. And there&apos;s the joy of seeing it used. For me, the second one lasts longer.&lt;/p&gt;
&lt;p&gt;That&apos;s what I optimize for now. Not the craft of the code, but the moment a customer&apos;s life gets a little better because of something I shipped.&lt;/p&gt;
&lt;h2&gt;Don&apos;t Judge, But Introspect&lt;/h2&gt;
&lt;p&gt;If you love hand-cutting dovetail joints, keep doing it. If writing code by hand brings you joy and flow, that&apos;s real and valid.&lt;/p&gt;
&lt;p&gt;But be honest with yourself about why.&lt;/p&gt;
&lt;p&gt;Are you writing code for the craft? For the beauty of it? For the feeling of mastery? That&apos;s fine. Paul would&apos;ve understood that completely.&lt;/p&gt;
&lt;p&gt;Or are you writing code to solve a problem for someone who doesn&apos;t care how you solved it? Because if so, reach for the power tools.&lt;/p&gt;
&lt;p&gt;Paul&apos;s hand-cut joints were beautiful. My table-saw cuts were fast. We&apos;d look at each other&apos;s work, nod, and go ride our electric unicycles around Cupertino.&lt;/p&gt;
&lt;p&gt;There was room for both of us then. There still is now. Just know which one you are, and why.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.bradshawfuneral.com/obituaries/paul-pelzl&quot;&gt;Rest in peace, Paul&lt;/a&gt;. You are sorely missed.&lt;/p&gt;
</content:encoded></item><item><title>Curiosity Is the Only Skill AI Can&apos;t Replace</title><link>https://www.augusteo.com/blog/ai-learning-study-curiosity/</link><guid isPermaLink="true">https://www.augusteo.com/blog/ai-learning-study-curiosity/</guid><description>Anthropic&apos;s study shows AI coding tools cut learning by 17%, but the real finding is that curiosity determines everything.</description><pubDate>Tue, 03 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Anthropic just published a study showing their own product makes developers worse. But buried in the data is the opposite finding: developers who asked &quot;how does this work?&quot; learned just as well as those who coded by hand.&lt;/p&gt;
&lt;p&gt;The difference wasn&apos;t AI. It was curiosity.&lt;/p&gt;
&lt;h2&gt;The Study&lt;/h2&gt;
&lt;p&gt;52 professional engineers learned a new Python library called Trio. Half used Claude, half coded by hand. Then they took a quiz.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;On a quiz that covered concepts they&apos;d used just a few minutes before, participants in the AI group scored 17% lower than those who coded by hand, or the equivalent of nearly two letter grades.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That&apos;s the headline number. But the averages hide the real story.&lt;/p&gt;
&lt;h2&gt;What Actually Mattered&lt;/h2&gt;
&lt;p&gt;The researchers identified six distinct patterns of AI usage. Three killed learning. Three preserved it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;The low-scoring patterns generally involved a heavy reliance on AI, either through code generation or debugging... They showed less independent thinking and more cognitive offloading.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The low scorers used AI like a slot machine. Type in a problem, get code out, move on. When it didn&apos;t work, they&apos;d paste the error back in and wait for the fix. No understanding required.&lt;/p&gt;
&lt;p&gt;The high scorers did something different:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;The participants who showed stronger mastery used AI assistance not just to produce code but to build comprehension while doing so—whether by asking follow-up questions, requesting explanations, or posing conceptual questions.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One group asked &quot;what should I write?&quot; The other asked &quot;how does this work?&quot;&lt;/p&gt;
&lt;h2&gt;The Risk and the Opportunity&lt;/h2&gt;
&lt;p&gt;The study found the biggest gap was in debugging:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;The largest gap in scores between the two groups was on debugging questions, suggesting that the ability to understand when code is incorrect and why it fails may be a particular area of concern.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That&apos;s the risk. Here&apos;s the opportunity.&lt;/p&gt;
&lt;p&gt;I&apos;m at Boon, building a preconstruction platform with React on Rails. I&apos;d used Rails for small projects, but never at production scale. For the last nine years I&apos;d been doing iOS (CoreData, direct SQL on device). Production Rails is a different beast: Active Record, migrations, the Rails console. All new territory.&lt;/p&gt;
&lt;p&gt;With AI, I learned it in weeks instead of months. By asking it to explain everything.&lt;/p&gt;
&lt;p&gt;When I needed to explore the production database through the Rails console (terrifying for someone who&apos;d spent a decade on mobile), AI generated the commands and told me which ones were safe. I wasn&apos;t just executing. I was building a mental model.&lt;/p&gt;
&lt;p&gt;The key: I always asked it to verify. &quot;Look this up online and double-check.&quot; It often corrected itself. When I wasn&apos;t sure, I asked my Rails-expert colleague. AI accelerated my learning, but I stayed curious.&lt;/p&gt;
&lt;h2&gt;Understanding Compounds&lt;/h2&gt;
&lt;p&gt;Every time you ask &quot;why does this work this way?&quot; you&apos;re building something AI can&apos;t give you directly.&lt;/p&gt;
&lt;p&gt;Architecture knowledge is just accumulated understanding of tradeoffs. Debugging skill is pattern recognition from past failures. Senior engineers use AI as a junior dev to supervise (they already have the instincts to know when the output is wrong).&lt;/p&gt;
&lt;p&gt;You can&apos;t shortcut this. But you can accelerate it (if you stay curious).&lt;/p&gt;
&lt;p&gt;After AI implements something for me, I ask it to explain what it just did. I ask it to write an informative PR message. I go back and forth until I understand. AI will never get bored with your questions. That&apos;s its superpower (if you use it).&lt;/p&gt;
&lt;h2&gt;The Junior Developer Problem&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Junior developers may rely on AI to complete tasks as fast as possible at the cost of skill development—and notably the ability to debug issues when something goes wrong.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If AI stunts skill formation, and juniors are the pipeline to seniors, we have a problem. Some companies now run &quot;AI-free learning sessions&quot; for new hires.&lt;/p&gt;
&lt;p&gt;But I don&apos;t think the solution is restricting AI. The solution is training people HOW to use it.&lt;/p&gt;
&lt;p&gt;The habit that matters: stay curious and take ownership. AI is a tool. Ask your seniors how they use it. Keep up with how it&apos;s evolving (I&apos;ve gone from Copilot to Claude Code to MCP servers to custom skills in two years). Try new things. See what works. Share what you learn.&lt;/p&gt;
&lt;p&gt;Every company needs someone to experiment so they can share their learning with everyone else. Be that person.&lt;/p&gt;
&lt;h2&gt;What This Means&lt;/h2&gt;
&lt;p&gt;Anthropic published research showing their own product has downsides.&lt;/p&gt;
&lt;p&gt;But the deeper lesson: AI is a learning accelerator or a learning replacement, depending on how you use it. The study participants who stayed curious scored just as well as those who coded by hand. The ones who delegated everything learned nothing.&lt;/p&gt;
&lt;p&gt;Understanding compounds. The future belongs to the curious.&lt;/p&gt;
&lt;p&gt;(via &lt;a href=&quot;https://www.anthropic.com/research/AI-assistance-coding-skills&quot;&gt;Anthropic Research&lt;/a&gt;)&lt;/p&gt;
</content:encoded></item><item><title>How I Made npm Impossible in Claude Code</title><link>https://www.augusteo.com/blog/enforcing-bun-with-hooks/</link><guid isPermaLink="true">https://www.augusteo.com/blog/enforcing-bun-with-hooks/</guid><description>After Claude ignored my CLAUDE.md instruction and switched to npm, I set up hooks that make it impossible for this to happen again.</description><pubDate>Fri, 30 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last week &lt;a href=&quot;/blog/claude-ignored-my-instructions&quot;&gt;Claude ignored my CLAUDE.md instructions&lt;/a&gt; and switched to npm in a Bun project. My advice was &quot;stay engaged&quot; and &quot;check the diffs.&quot;&lt;/p&gt;
&lt;p&gt;That&apos;s still good advice. But I found a better solution: make it impossible.&lt;/p&gt;
&lt;h2&gt;What Are Hooks?&lt;/h2&gt;
&lt;p&gt;Claude Code has a feature called &lt;a href=&quot;https://code.claude.com/docs/en/hooks&quot;&gt;hooks&lt;/a&gt;. They&apos;re shell commands that run automatically at specific points in Claude&apos;s workflow.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;PreToolUse&lt;/code&gt; is the one we care about. It runs before Claude executes any tool. If your hook script exits with code 2, the action is blocked. Claude sees your error message and has to find another approach.&lt;/p&gt;
&lt;p&gt;Think of it as a bouncer. Claude says &quot;I want to run &lt;code&gt;npm install&lt;/code&gt;.&quot; The hook checks the command, sees &quot;npm,&quot; and says &quot;No. Try again.&quot;&lt;/p&gt;
&lt;p&gt;You configure hooks in &lt;code&gt;.claude/settings.json&lt;/code&gt; in your project.&lt;/p&gt;
&lt;h2&gt;Hook 1: Block npm Commands&lt;/h2&gt;
&lt;p&gt;This one-liner intercepts every Bash command and blocks anything containing npm, yarn, npx, or pnpm:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;hooks&quot;: {
    &quot;PreToolUse&quot;: [
      {
        &quot;matcher&quot;: &quot;Bash&quot;,
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;jq -r &apos;.tool_input.command // \&quot;\&quot;&apos; | grep -qE &apos;\\b(npm|yarn|npx|pnpm)\\b&apos; &amp;amp;&amp;amp; echo &apos;BLOCKED: This project uses Bun. Use bun instead of npm/yarn/npx/pnpm.&apos; &amp;gt;&amp;amp;2 &amp;amp;&amp;amp; exit 2 || exit 0&quot;
          }
        ]
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;How it works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;jq&lt;/code&gt; extracts the command from Claude&apos;s input&lt;/li&gt;
&lt;li&gt;&lt;code&gt;grep&lt;/code&gt; checks for forbidden package managers (word boundaries prevent false matches like &quot;pnpmrc&quot;)&lt;/li&gt;
&lt;li&gt;If found: print error to stderr, exit 2 (block)&lt;/li&gt;
&lt;li&gt;If not: exit 0 (allow)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When Claude tries to run &lt;code&gt;npm install&lt;/code&gt;, it sees:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;BLOCKED: This project uses Bun. Use bun instead of npm/yarn/npx/pnpm.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then it retries with &lt;code&gt;bun install&lt;/code&gt;. No human intervention needed.&lt;/p&gt;
&lt;h2&gt;Hook 2: Block npm in Dockerfiles&lt;/h2&gt;
&lt;p&gt;The first hook catches terminal commands. But my original incident was Claude editing a Dockerfile to use &lt;code&gt;npm run start&lt;/code&gt;, a Write operation, not a Bash command.&lt;/p&gt;
&lt;p&gt;So we need a second hook that intercepts file writes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;hooks&quot;: {
    &quot;PreToolUse&quot;: [
      {
        &quot;matcher&quot;: &quot;Write|Edit&quot;,
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;$CLAUDE_PROJECT_DIR/.claude/hooks/block-npm-in-files.sh&quot;
          }
        ]
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The script (&lt;code&gt;.claude/hooks/block-npm-in-files.sh&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash
set -e

input=$(cat)
file_path=$(echo &quot;$input&quot; | jq -r &apos;.tool_input.file_path // &quot;&quot;&apos;)
content=$(echo &quot;$input&quot; | jq -r &apos;.tool_input.content // .tool_input.new_string // &quot;&quot;&apos;)

# Only check files where package manager references matter
case &quot;$file_path&quot; in
  *Dockerfile*|*docker-compose*.yml|*.github/workflows/*.yml)
    if echo &quot;$content&quot; | grep -qE &apos;\b(npm|yarn|npx|pnpm)\b&apos;; then
      echo &quot;BLOCKED: You&apos;re writing npm/yarn references to $file_path.&quot; &amp;gt;&amp;amp;2
      echo &quot;This project uses Bun. Find a Bun-native solution.&quot; &amp;gt;&amp;amp;2
      exit 2
    fi
    ;;
esac

exit 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make it executable with &lt;code&gt;chmod +x .claude/hooks/block-npm-in-files.sh&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now if Claude tries to write &lt;code&gt;CMD [&quot;npm&quot;, &quot;run&quot;, &quot;start&quot;]&lt;/code&gt; to a Dockerfile, it gets blocked. It has to find a Bun-native solution (like &lt;code&gt;CMD [&quot;bun&quot;, &quot;run&quot;, &quot;start&quot;]&lt;/code&gt;).&lt;/p&gt;
&lt;h2&gt;The Difference&lt;/h2&gt;
&lt;p&gt;CLAUDE.md is guidance. Claude can ignore it under pressure, especially during long debugging sessions where the immediate problem overshadows standing rules.&lt;/p&gt;
&lt;p&gt;Hooks are guardrails. They&apos;re deterministic. Every time Claude tries to use npm, it gets blocked. No exceptions. No &quot;I know you said Bun but this would be faster.&quot;&lt;/p&gt;
&lt;p&gt;The pattern works for any project convention:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Block &lt;code&gt;rm -rf&lt;/code&gt; commands&lt;/li&gt;
&lt;li&gt;Require tests to pass before commits&lt;/li&gt;
&lt;li&gt;Prevent modifications to sensitive files&lt;/li&gt;
&lt;li&gt;Enforce directory structure&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Check the &lt;a href=&quot;https://code.claude.com/docs/en/hooks&quot;&gt;official hooks documentation&lt;/a&gt; for more examples.&lt;/p&gt;
&lt;h2&gt;Does It Work?&lt;/h2&gt;
&lt;p&gt;I tested these hooks in a fresh Claude Code session. Eight test cases:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;yarn add lodash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npx create-react-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bun install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read &lt;code&gt;.pnpmrc&lt;/code&gt; file&lt;/td&gt;
&lt;td&gt;No false positive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write &lt;code&gt;npm start&lt;/code&gt; to Dockerfile&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write &lt;code&gt;bun start&lt;/code&gt; to Dockerfile&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write &lt;code&gt;console.log(&apos;npm&apos;)&lt;/code&gt; to &lt;code&gt;.js&lt;/code&gt; file&lt;/td&gt;
&lt;td&gt;Works (not a config file)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;All eight passed. The hooks block what they should block and allow what they should allow.&lt;/p&gt;
&lt;p&gt;You can find the &lt;a href=&quot;https://github.com/augusteo/experiments/tree/main/claude-hooks-npm-blocker&quot;&gt;test setup on GitHub&lt;/a&gt; if you want to try it yourself.&lt;/p&gt;
&lt;p&gt;I&apos;m still watching Claude work. But now I&apos;m not watching for npm. That problem is solved.&lt;/p&gt;
</content:encoded></item><item><title>Gemini Hallucinates Less When You Give It Python</title><link>https://www.augusteo.com/blog/til-gemini-agentic-vision/</link><guid isPermaLink="true">https://www.augusteo.com/blog/til-gemini-agentic-vision/</guid><description>Testing Gemini 3 Flash&apos;s new agentic vision feature with code execution</description><pubDate>Tue, 27 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When &lt;a href=&quot;https://blog.google/innovation-and-ai/technology/developers-tools/agentic-vision-gemini-3-flash/&quot;&gt;Google announced &quot;agentic vision&quot;&lt;/a&gt; for Gemini 3 Flash (the model can run Python code to analyze images), I had to test it. I ran 6 different tests. The results surprised me.&lt;/p&gt;
&lt;h2&gt;Summary: Code OFF vs Code ON&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;Code OFF&lt;/th&gt;
&lt;th&gt;Code ON&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rebar counting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&quot;60 rebar&quot;&lt;/td&gt;
&lt;td&gt;&quot;0 rebar&quot; (correct: image shows wood)&lt;/td&gt;
&lt;td&gt;✅ Hallucination prevented&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pipe counting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&quot;5 pipes&quot;&lt;/td&gt;
&lt;td&gt;&quot;0 pipes&quot; (looks like cables/supports)&lt;/td&gt;
&lt;td&gt;✅ Hallucination prevented&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Floor plan (trees)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10 trees (correct)&lt;/td&gt;
&lt;td&gt;12 trees (wrong: zooms show 7+3=10)&lt;/td&gt;
&lt;td&gt;❌ Overcounted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Floor plan (doors)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;21 doors&lt;/td&gt;
&lt;td&gt;18 doors + room-by-room list&lt;/td&gt;
&lt;td&gt;✅ More detailed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lighting fittings&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;17 new, 12 original&lt;/td&gt;
&lt;td&gt;17 new, 12 original + per-room breakdown&lt;/td&gt;
&lt;td&gt;✅ Same count, better structure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PPE detection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lists items per worker&lt;/td&gt;
&lt;td&gt;Lists items with specific colors/details&lt;/td&gt;
&lt;td&gt;✅ More specific&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dashboard extraction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Extracts values&lt;/td&gt;
&lt;td&gt;Extracts values + axis scales&lt;/td&gt;
&lt;td&gt;✅ More complete&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Key findings:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hallucination prevention&lt;/strong&gt; is the biggest win (60→0 on rebar, 5→0 on pipes)&lt;/li&gt;
&lt;li&gt;Code execution makes the model &lt;strong&gt;more conservative&lt;/strong&gt; (says &quot;none&quot; when unsure)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Not perfect&lt;/strong&gt;: palm trees went from correct (10) to wrong (12), even with zoomed evidence showing 7+3=10&lt;/li&gt;
&lt;li&gt;Every answer comes with &lt;strong&gt;visual evidence&lt;/strong&gt; (38 zoomed crops across 6 tests)&lt;/li&gt;
&lt;li&gt;Outputs are &lt;strong&gt;structured by region&lt;/strong&gt; (Worker 1 vs 2, room-by-room, chart-by-chart)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What I Tested&lt;/h2&gt;
&lt;p&gt;I ran the same prompts with code execution OFF and ON. The difference isn&apos;t a parameter (it&apos;s a tool):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from google import genai
from google.genai import types

client = genai.Client(api_key=os.getenv(&quot;GOOGLE_API_KEY&quot;))

# Code execution enabled
config = types.GenerateContentConfig(
    tools=[types.Tool(code_execution=types.ToolCodeExecution())]
)

response = client.models.generate_content(
    model=&quot;gemini-3-flash-preview&quot;,
    contents=[image, prompt],
    config=config,
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The response includes &lt;code&gt;executable_code&lt;/code&gt; and &lt;code&gt;inline_data&lt;/code&gt; (generated images) when the model chooses to analyze visually.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Test 1: Rebar Counting (Hallucination Prevention)&lt;/h2&gt;
&lt;p&gt;This was the most dramatic result. I sent a construction site image with the prompt &quot;count the rebar.&quot;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-rebar.jpg&quot; alt=&quot;The woodworker image where Gemini hallucinated 60 rebar&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Code OFF:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;text&quot;: &quot;You can see 60 rebar (metal reinforcement bars) in this picture.&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Code ON:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;text&quot;: &quot;Based on a careful examination of the image, including close-ups of the background and the worker&apos;s table, there are no rebar (metal reinforcement bars typically used for concrete) visible. The image depicts a construction worker working with large wooden beams and various tools (circular saw, drill, clamps, etc.) inside what appears to be a structural steel building. The visible metal elements are structural steel beams, bracing rods, and tools, rather than rebar.\n\nFinal count: 0 rebar.&quot;,
  &quot;images_generated_count&quot;: 7
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The image showed a woodworker with wooden beams. Not rebar. Without code execution, Gemini confidently hallucinated &quot;60 rebar.&quot; With code execution, it zoomed into 7 regions, examined them, and correctly identified wood.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The code Gemini executed:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import PIL.Image
import PIL.ImageDraw

img = PIL.Image.open(&apos;input_file_0.jpeg&apos;)
width, height = img.size

# Looking for rebar - often found in concrete construction, but this looks like wood.
# Let&apos;s zoom into various parts to be sure.
regions = [
    [0, 0, 1000, 500],   # Top half (ceiling/background)
    [500, 0, 1000, 1000], # Bottom half (floor/bench)
    [0, 0, 500, 500],    # Top left
    [0, 500, 500, 1000],  # Top right
]

# Convert normalized to pixel coordinates
def norm_to_pixel(norm_coords, w, h):
    return [norm_coords[0] * h / 1000, norm_coords[1] * w / 1000,
            norm_coords[2] * h / 1000, norm_coords[3] * w / 1000]

for i, reg in enumerate(regions):
    p_reg = norm_to_pixel(reg, width, height)
    crop = img.crop((p_reg[1], p_reg[0], p_reg[3], p_reg[2]))
    crop.save(f&apos;zoom_{i}.jpeg&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The model divides the image into regions, crops each one, and examines them separately. It&apos;s essentially giving itself a closer look at different parts of the image before committing to an answer.&lt;/p&gt;
&lt;p&gt;In a second code block, it zooms into the background specifically:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Zoom into the background more clearly
regions_bg = [
    [350, 600, 600, 900], # Right background
    [350, 300, 600, 600], # Center background
    [350, 0, 600, 300],   # Left background
]

for i, reg in enumerate(regions_bg):
    p_reg = norm_to_pixel(reg, width, height)
    crop = img.crop((p_reg[1], p_reg[0], p_reg[3], p_reg[2]))
    crop.save(f&apos;bg_zoom_{i}.jpeg&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;The model&apos;s zoomed regions:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-rebar-zoom1.png&quot; alt=&quot;Rebar zoom 1&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-rebar-zoom2.png&quot; alt=&quot;Rebar zoom 2&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-rebar-zoom3.png&quot; alt=&quot;Rebar zoom 3&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-rebar-zoom4.png&quot; alt=&quot;Rebar zoom 4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-rebar-zoom5.png&quot; alt=&quot;Rebar zoom 5&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-rebar-zoom6.png&quot; alt=&quot;Rebar zoom 6&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-rebar-zoom7.png&quot; alt=&quot;Rebar zoom 7&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Test 2: Pipe Counting (Conservative Mode)&lt;/h2&gt;
&lt;p&gt;Another case where code execution made the model more conservative.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-pipes.jpg&quot; alt=&quot;Welder with pipes in background&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Code OFF:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;text&quot;: &quot;This image contains five pipes.&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Code ON:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;text&quot;: &quot;Based on a careful examination of the image, there are no clearly identifiable pipes present. The background contains several blurred structural elements, such as vertical rods and curved loops, which appear more like cables or solid metal supports than pipes. The welder is using a stick welding electrode, which is a solid rod, and is working on what looks like a flat or rectangular piece of metal.\n\nFinal Answer: 0&quot;,
  &quot;images_generated_count&quot;: 3
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The model questioned whether the objects were actually pipes. It decided they looked more like cables or supports.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The code Gemini executed:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import PIL.Image
import PIL.ImageDraw

img = PIL.Image.open(&apos;input_file_0.jpeg&apos;)
width, height = img.size

# Define potential pipe areas
objs = [
    {&apos;box_2d&apos;: [310, 560, 680, 640], &apos;label&apos;: &apos;potential pipe 1&apos;},
    {&apos;box_2d&apos;: [0, 640, 420, 950], &apos;label&apos;: &apos;potential pipe background right&apos;},
    {&apos;box_2d&apos;: [480, 500, 780, 750], &apos;label&apos;: &apos;welding rod (pipe?)&apos;},
]

# Create crops to see better
for i, obj in enumerate(objs):
    ymin, xmin, ymax, xmax = obj[&apos;box_2d&apos;]
    crop = img.crop((xmin * width / 1000, ymin * height / 1000,
                     xmax * width / 1000, ymax * height / 1000))
    crop.save(f&apos;crop_{i}.png&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The model identifies potential pipe locations, crops them out, examines each one, then decides &quot;these look more like cables.&quot; This could be seen as overly cautious, but for construction QC, conservative beats confident-but-wrong.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The model&apos;s zoomed regions:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-pipes-zoom1.png&quot; alt=&quot;Pipes zoom 1&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-pipes-zoom2.png&quot; alt=&quot;Pipes zoom 2&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-pipes-zoom3.png&quot; alt=&quot;Pipes zoom 3&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Test 3: Floor Plan Counting (Trees &amp;amp; Doors)&lt;/h2&gt;
&lt;p&gt;A floor plan with pool, garage, and multiple rooms.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-floor-plan.jpg&quot; alt=&quot;Floor plan with trees and doors&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Code OFF:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;text&quot;: &quot;There are exactly 10 trees and 21 doors shown here.&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Code ON:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;text&quot;: &quot;Based on the provided floor plan, here are the counts for trees and doors:\n\n### Trees: 12\nThere are 12 palm trees visible around the exterior of the property:\n- 8 palm trees are lined up along the top edge of the floor plan.\n- 4 palm trees are located in a row at the bottom right corner near the patio.\n- (Note: There are several other smaller green plants or bushes located on the terraces, in the gym, and in the bathroom, but these are generally represented as potted plants or small bushes rather than trees.)\n\n### Doors: 18\nThere are 18 doors (including door leaves and sliding panels) identified by their typical architectural symbols (arcs for swinging doors and parallel lines for sliding doors):\n1. 3 doors in the top-left exterior walkway area (one from the driveway, one to the terrace, and one from the laundry room).\n2. 1 door between the laundry room and the interior hallway.\n3. 1 door from the interior hallway into the gym.\n4. 1 door from the interior hallway to the terrace (located between the living room and the gym).\n5. 2 doors (a double door) from the living room to the terrace.\n6. 1 door from the main hall to the terrace.\n7. 1 door from the main hall leading to the outside on the right.\n8. 1 door from the main hall into the wine cellar.\n9. 1 door from the main hall into the guest bathroom.\n10. 1 door from the main hall into the guest bedroom.\n11. 1 door (sliding glass door) from the guest bedroom to the patio.\n12. 1 door from the gym to the garage.\n13. 1 door from the gym to the bathroom.\n14. 1 door from the gym to the terrace.\n15. 1 door from the bathroom to the terrace.\n16. 1 door from the driveway into the corridor leading to the terrace.\n\nTotal: 17 swinging door leaves (arcs) + 1 sliding door = 18 doors in total.&quot;,
  &quot;images_generated_count&quot;: 8
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Code execution gave a room-by-room breakdown, but got the tree count wrong. The zoomed images clearly show 7 trees on top and 3 on the bottom right (total: 10), yet the model reported 8+4=12. Code OFF was actually correct here. The model does distinguish between trees and potted plants, and between swinging doors (arcs) and sliding doors (parallel lines).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The code Gemini executed:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import PIL.Image
import PIL.ImageDraw

img = PIL.Image.open(&apos;input_file_0.jpeg&apos;)
width, height = img.size

# Detect trees and doors.
objs = [
    {&apos;box_2d&apos;: [0, 140, 180, 520], &apos;label&apos;: &apos;top_trees&apos;},
    {&apos;box_2d&apos;: [740, 800, 995, 965], &apos;label&apos;: &apos;bottom_right_trees&apos;},
    {&apos;box_2d&apos;: [435, 212, 555, 330], &apos;label&apos;: &apos;left_doors&apos;},
    {&apos;box_2d&apos;: [230, 395, 385, 480], &apos;label&apos;: &apos;laundry_living_doors&apos;},
    {&apos;box_2d&apos;: [280, 750, 485, 955], &apos;label&apos;: &apos;hall_doors&apos;},
    {&apos;box_2d&apos;: [540, 240, 750, 455], &apos;label&apos;: &apos;gym_bath_doors&apos;},
    {&apos;box_2d&apos;: [380, 595, 450, 770], &apos;label&apos;: &apos;top_terrace_doors&apos;},
    {&apos;box_2d&apos;: [550, 765, 700, 900], &apos;label&apos;: &apos;guest_bedroom_doors&apos;}
]

def save_crops(img, objs, prefix):
    for i, obj in enumerate(objs):
        ymin, xmin, ymax, xmax = obj[&apos;box_2d&apos;]
        left = xmin * width / 1000
        top = ymin * height / 1000
        right = xmax * width / 1000
        bottom = ymax * height / 1000
        crop = img.crop((left, top, right, bottom))
        crop.save(f&apos;{prefix}_{i}.png&apos;)

save_crops(img, objs, &apos;crop&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The model creates labeled regions (top_trees, hall_doors, gym_bath_doors, etc.) and crops each one. This is how it can give a room-by-room breakdown in its response.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The model&apos;s zoomed regions:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-floor-plan-zoom1.png&quot; alt=&quot;Floor plan zoom 1&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-floor-plan-zoom2.png&quot; alt=&quot;Floor plan zoom 2&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-floor-plan-zoom3.png&quot; alt=&quot;Floor plan zoom 3&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-floor-plan-zoom4.png&quot; alt=&quot;Floor plan zoom 4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-floor-plan-zoom5.png&quot; alt=&quot;Floor plan zoom 5&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-floor-plan-zoom6.png&quot; alt=&quot;Floor plan zoom 6&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-floor-plan-zoom7.png&quot; alt=&quot;Floor plan zoom 7&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-floor-plan-zoom8.png&quot; alt=&quot;Floor plan zoom 8&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Test 4: Lighting Fittings Count&lt;/h2&gt;
&lt;p&gt;A lighting plan (blue = new fittings, red = original).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-fittings.webp&quot; alt=&quot;Lighting plan with fittings&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Code OFF:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;text&quot;: &quot;Based on the lighting plan provided:\n\n- New / Extra Fittings (blue squares and rectangles): 17\n- Original Fittings (red circles): 12&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Code ON:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;text&quot;: &quot;Based on the lighting plan provided, here is the exact count for each category of fittings:\n\n### New / Extra Fittings (Blue squares and rectangles): 17\n- Entrance (top center): 2 squares (one on each side of the door).\n- Top-left room (dining): 2 squares (one in the top-left corner and one in the bottom-right area).\n- Top-right room (living): 5 fittings total (1 square in the top-left, 2 long horizontal rectangles near the sofa, 1 square below the lower rectangle, and 1 square near the TV).\n- Kitchen (bottom-left): 4 squares (1 on the top counter, 1 on the left counter, and 2 on the island).\n- Central hallway: 1 long vertical rectangle located between the kitchen and the bottom-right dining area.\n- Bottom-right room (dining): 3 squares (1 at the top entrance, 1 in the center of the table, and 1 on the right wall).\n\n### Original Fittings (Red circles): 12\n- Top-left room (dining): 3 circles (one near the top wall, one in the center of the round table, and one in the bottom-left corner).\n- Top-right room (living): 3 circles (one in the top-right corner, one large circle in the center, and one in the bottom-left area above the partition wall).\n- Kitchen (bottom-left): 4 circles (one in the top-left corner, one in the middle of the floor, one in the bottom-left corner, and one near the bottom of the island).\n- Bottom-right room (dining): 2 circles (one in the top-right corner and one in the bottom-right corner).\n\nSummary:\n- New / Extra Fittings: 17\n- Original Fittings: 12&quot;,
  &quot;images_generated_count&quot;: 4
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Same totals (17 new, 12 original). But Code OFF just says &quot;17.&quot; Code ON says &quot;Kitchen: 4 squares (1 on the top counter, 1 on the left counter, and 2 on the island).&quot; For construction audits, that&apos;s the difference between a number and a checklist.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The code Gemini executed:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import PIL.Image
import PIL.ImageDraw

img = PIL.Image.open(&apos;input_file_0.jpeg&apos;)
width, height = img.size

# Detect objects manually for counting and zoom
# Red Circles (Original Fittings)
# Blue Squares/Rectangles (New / Extra Fittings)

objs = [
    # Red circles - with exact pixel coordinates
    {&apos;box_2d&apos;: [210, 345, 265, 385], &apos;label&apos;: &apos;original_red&apos;}, # TL room top
    {&apos;box_2d&apos;: [435, 240, 485, 285], &apos;label&apos;: &apos;original_red&apos;}, # TL room bottom left
    {&apos;box_2d&apos;: [160, 730, 215, 770], &apos;label&apos;: &apos;original_red&apos;}, # TR room top right
    # ... (11 red circles total)

    # Blue shapes - with exact pixel coordinates
    {&apos;box_2d&apos;: [100, 350, 160, 395], &apos;label&apos;: &apos;new_blue_sq&apos;}, # Entrance left
    {&apos;box_2d&apos;: [100, 435, 160, 480], &apos;label&apos;: &apos;new_blue_sq&apos;}, # Entrance right
    {&apos;box_2d&apos;: [195, 550, 235, 705], &apos;label&apos;: &apos;new_blue_rect&apos;}, # TR room upper rect
    # ... (17 blue shapes total)
]

# Create crops by quadrant to double check
img.crop((0, 0, width//2, height//2)).save(&apos;top_left.jpeg&apos;)
img.crop((width//2, 0, width, height//2)).save(&apos;top_right.jpeg&apos;)
img.crop((0, height//2, width//2, height)).save(&apos;bottom_left.jpeg&apos;)
img.crop((width//2, height//2, width, height)).save(&apos;bottom_right.jpeg&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The model creates a detailed inventory of every fitting with pixel coordinates, distinguishing between squares, rectangles, and circles. Then it crops the image into quadrants to verify its counts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The model&apos;s zoomed regions:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-fittings-zoom1.png&quot; alt=&quot;Fittings zoom 1&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-fittings-zoom2.png&quot; alt=&quot;Fittings zoom 2&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-fittings-zoom3.png&quot; alt=&quot;Fittings zoom 3&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-fittings-zoom4.png&quot; alt=&quot;Fittings zoom 4&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Test 5: PPE Detection (Color Accuracy)&lt;/h2&gt;
&lt;p&gt;Two construction workers with various safety equipment.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-construction-worker.jpg&quot; alt=&quot;Construction workers with PPE&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Code OFF:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;text&quot;: &quot;Based on the image, the Personal Protective Equipment (PPE) visible on the workers includes:\n\nFor the worker kneeling in the background:\n- Hard Hat: An orange hard hat for head protection.\n- High-Visibility Vest: An orange safety vest with reflective strips to ensure visibility.\n- Work Gloves: Dark gloves to protect his hands while handling tools and materials.\n- Work Boots: Sturdy, dark-colored boots suitable for a construction environment.\n\nFor the worker standing in the foreground:\n- Face/Head Covering: A light blue cloth or balaclava covering the face and head, likely for protection against sun, dust, or wind.\n- High-Visibility Vest: An orange safety vest with reflective elements.\n- Work Gloves: Dark protective gloves on his hands.\n- Safety Boots: Tall, dark-colored waterproof or safety boots.&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Code ON:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;text&quot;: &quot;Based on the image, the Personal Protective Equipment (PPE) items visible on the construction workers include:\n\n### Worker 1 (Kneeling):\n- Hard Hat: An orange safety helmet.\n- High-Visibility Vest: An orange safety vest featuring yellow/silver reflective strips.\n- Safety Boots: Dark, heavy-duty work boots.\n- Head/Neck Covering: A white cloth or scarf worn under the hard hat, likely for sun protection and sweat management.\n\n### Worker 2 (Standing):\n- High-Visibility Vest: An orange safety vest with yellow/silver reflective strips.\n- Safety Gloves: Blue and black protective gloves.\n- Safety Boots: Dark, tall work boots (possibly rubber or waterproof).\n- Face Mask/Neck Gaiter: A light blue fabric covering the face and neck.\n- Face Shield / Welding Mask: A dark protective shield or mask worn on the head, currently flipped up.\n\nBoth workers are also wearing full-length clothing (long sleeves and pants), which provides additional protection against abrasions and environmental factors on a construction site.&quot;,
  &quot;images_generated_count&quot;: 2
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Code ON added specific colors (blue and black gloves, yellow/silver reflective strips) and noticed details like the face shield flipped up on Worker 2&apos;s head.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The code Gemini executed:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import PIL.Image
import PIL.ImageDraw

img = PIL.Image.open(&apos;input_file_0.jpeg&apos;)
width, height = img.size

# List workers and their PPE items to zoom in on
workers_and_ppe = [
    {&apos;box_2d&apos;: [113, 471, 424, 603], &apos;label&apos;: &apos;worker_1&apos;},
    {&apos;box_2d&apos;: [388, 452, 861, 561], &apos;label&apos;: &apos;worker_2&apos;},
    {&apos;box_2d&apos;: [114, 537, 185, 586], &apos;label&apos;: &apos;hard_hat_1&apos;},
    {&apos;box_2d&apos;: [179, 482, 332, 592], &apos;label&apos;: &apos;hi_vis_vest_1&apos;},
    {&apos;box_2d&apos;: [484, 477, 663, 558], &apos;label&apos;: &apos;hi_vis_vest_2&apos;},
    {&apos;box_2d&apos;: [547, 451, 603, 487], &apos;label&apos;: &apos;gloves_2&apos;},
    {&apos;box_2d&apos;: [416, 492, 513, 545], &apos;label&apos;: &apos;face_mask_2&apos;},
    {&apos;box_2d&apos;: [779, 487, 865, 545], &apos;label&apos;: &apos;boots_2&apos;}
]

def save_crop(img, box, name):
    ymin, xmin, ymax, xmax = box
    left = xmin * width / 1000
    top = ymin * height / 1000
    right = xmax * width / 1000
    bottom = ymax * height / 1000
    crop = img.crop((left, top, right, bottom))
    crop.save(name)

# Crop workers for better visibility
save_crop(img, [100, 450, 450, 650], &apos;worker1_zoom.png&apos;)
save_crop(img, [350, 430, 880, 580], &apos;worker2_zoom.png&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The model identifies specific PPE items (hard_hat_1, gloves_2, face_mask_2) with bounding boxes, then crops each worker separately for closer inspection. This is why Code ON can report specific colors and details.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The model&apos;s zoomed regions:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-worker-zoom.png&quot; alt=&quot;Worker 1 zoom&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-worker-zoom2.png&quot; alt=&quot;Worker 2 zoom&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Test 6: Dashboard Data Extraction (Axis Reading)&lt;/h2&gt;
&lt;p&gt;A web analytics dashboard with dense data.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-chart.jpg&quot; alt=&quot;Analytics dashboard&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Code OFF:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;text&quot;: &quot;Based on the dashboard visible on the laptop screen, here are the extracted labels and data values:\n\n### Top Header\n- Title: USERS: LAST 7 DAYS USING MEDIAN\n\n### Top-Left Chart: LOAD TIME VS BOUNCE RATE\n- Metrics/Legend: Page Load (LUX) (Blue histogram), Bounce Rate (Pink line)\n- Annotated Data Point: Median Page Load (LUX): 2.056s, Bounce Rate 7s 57.1%\n- Y-Axis (Left - Counts): 0, 15K, 30K, 45K, 50K, 75K\n- Y-Axis (Right - Percentage): 0%, 20%, 40%, 60%, 80%, 100%\n- X-Axis (Seconds): 0, 2.5, 5, 7.5, 10, 12.5, 15, 17.5\n\n### Top-Right Chart: START RENDER VS BOUNCE RATE\n- Median Start Render (LUX): 1.031s\n- Y-Axis (Left - Counts): 0, 8K, 16K, 24K, 32K, 40K\n\n### Bottom-Left Section: PAGE VIEWS VS ONLOAD\n- Page Load (LUX): 0.7s\n- Page Views (LUX): 2.7Mpvs\n- Bounce Rate (LUX): 40.6%\n\n### Bottom-Right Section: SESSIONS\n- Sessions (LUX): 479K\n- Session Length (LUX): 17min\n- PVs Per Session (LUX): 2pvs\n\n### Additional Interface Elements\n- Options: Every quadrant features an OPTIONS link.\n- Notification: Chat/help icon with red notification bubble showing 1.&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Code ON:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;text&quot;: &quot;The dashboard titled USERS: LAST 7 DAYS USING MEDIAN displays the following key data points:\n\n### 1. LOAD TIME VS BOUNCE RATE\n- Median Page Load (LUX): 2.056s\n- Specific Callout: Bounce Rate at 7s is 57.1%.\n- Axes &amp;amp; Legend:\n  - X-axis (Time): 0 to 17.5 seconds (increments of 2.5s).\n  - Y-axis (Left - Count): 0, 15K, 30K, 45K, 60K, 75K.\n  - Y-axis (Right - Bounce Rate): 0% to 100% (increments of 20%).\n  - Legend: Page Load (LUX) is represented by blue bars; Bounce Rate is represented by a pink line.\n\n### 2. START RENDER VS BOUNCE RATE\n- Median Start Render (LUX): 1.031s\n- Axes &amp;amp; Legend:\n  - X-axis (Time): 0 to 5 seconds.\n  - Y-axis (Left - Count): 0, 8K, 16K, 24K, 32K, 40K.\n  - Y-axis (Right - Bounce Rate): 0% to 100% (increments of 20%).\n\n### 3. PAGE VIEWS VS ONLOAD\n- Page Load (LUX): 0.7s\n- Page Views (LUX): 2.7Mpvs\n- Bounce Rate (LUX): 40.6%\n- Chart Metrics:\n  - Left Y-axis (Load Time): 0.4s, 0.6s, 0.8s, 1s.\n  - Inner Right Y-axis (Page Views): 200K, 300K, 400K, 500K.\n  - Outer Right Y-axis (Bounce Rate): 40%, 60%, 80%, 100%.\n\n### 4. SESSIONS\n- Sessions (LUX): 479K\n- Session Length (LUX): 17min\n- PVs Per Session (LUX): 2pvs\n- Chart Metrics:\n  - Left Y-axis (PVs): 1.6 pvs, 2.4 pvs, 3.2 pvs, 4 pvs.\n  - Inner Right Y-axis (Sessions): 40K, 60K, 80K, 100K.\n  - Outer Right Y-axis (Session Length): 24 min, 32 min, 40 min.&quot;,
  &quot;images_generated_count&quot;: 5
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both modes extracted detailed data. Code ON organized it more systematically and captured dual Y-axis scales.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The code Gemini executed:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import PIL.Image
import PIL.ImageDraw

img = PIL.Image.open(&apos;input_file_0.jpeg&apos;)
width, height = img.size

# List of regions to crop for better visibility of data points and labels
crops = [
    [150, 80, 260, 450],   # Dashboard title and top-left header
    [260, 100, 600, 480],  # Top-left chart (LOAD TIME VS BOUNCE RATE)
    [260, 500, 600, 880],  # Top-right chart (START RENDER VS BOUNCE RATE)
    [650, 100, 960, 480],  # Bottom-left section (PAGE VIEWS VS ONLOAD)
    [650, 500, 960, 880]   # Bottom-right section (SESSIONS)
]

def get_crop(img, box_norm):
    ymin, xmin, ymax, xmax = box_norm
    left = xmin * width / 1000
    top = ymin * height / 1000
    right = xmax * width / 1000
    bottom = ymax * height / 1000
    return img.crop((left, top, right, bottom))

for i, crop_box in enumerate(crops):
    crop_img = get_crop(img, crop_box)
    crop_img.save(f&apos;crop_{i}.png&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The model divides the dashboard into logical sections (title, each of 4 chart areas) and crops each one. This allows it to read small text and axis labels more accurately.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The model&apos;s zoomed regions:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-chart-zoom.png&quot; alt=&quot;Chart zoom 1&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-chart-zoom2.png&quot; alt=&quot;Chart zoom 2&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-chart-zoom3.png&quot; alt=&quot;Chart zoom 3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-chart-zoom4.png&quot; alt=&quot;Chart zoom 4&quot; /&gt; &lt;img src=&quot;https://www.augusteo.com/assets/blog/til-gemini-agentic-vision/gemini-chart-zoom5.png&quot; alt=&quot;Chart zoom 5&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;What This Means&lt;/h2&gt;
&lt;p&gt;From 6 tests, here&apos;s what I learned:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Hallucination prevention is the real win.&lt;/strong&gt; Getting 60 vs 0 wrong (rebar) or 5 vs 0 (pipes) is a bigger problem than miscounting by 2. But code execution isn&apos;t magic: it got the palm tree count wrong (10→12) even while looking at zoomed images that clearly showed 10.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Code execution makes the model more conservative.&lt;/strong&gt; In 2 of 6 tests, it gave lower counts or &quot;none found&quot; when it looked closely. For high-stakes inspection, conservative beats confident-but-wrong.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Per-region breakdown is auditable.&lt;/strong&gt; When the model says &quot;12 trees,&quot; it shows you which 8 are on the top edge and which 4 are in the bottom right corner.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Structured output is more useful.&lt;/strong&gt; Worker 1 vs Worker 2, Room-by-room counts, Chart-by-chart extraction.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The code itself is informative.&lt;/strong&gt; You can see exactly what regions the model examined and what it labeled them. The &lt;code&gt;box_2d&lt;/code&gt; coordinates create an audit trail.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is significant. The model now shows its work.&lt;/p&gt;
&lt;p&gt;The code is on &lt;a href=&quot;https://github.com/augusteo/experiments&quot;&gt;GitHub&lt;/a&gt; if you want to run your own tests.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Appendix: Complete API Response&lt;/h2&gt;
&lt;p&gt;Here&apos;s what the full API response looks like (base64 image data truncated for readability):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;success&quot;: true,
  &quot;text&quot;: &quot;Based on a careful examination of the image, including close-ups of the background and the worker&apos;s table, there are no **rebar** (metal reinforcement bars typically used for concrete) visible. The image depicts a construction worker working with large wooden beams and various tools (circular saw, drill, clamps, etc.) inside what appears to be a structural steel building. The visible metal elements are structural steel beams, bracing rods, and tools, rather than rebar.\n\nFinal count: 0 rebar.&quot;,
  &quot;code_executed&quot;: [
    &quot;import PIL.Image\nimport PIL.ImageDraw\n\n# Load the image to get dimensions\nimg = PIL.Image.open(&apos;input_file_0.jpeg&apos;)\nwidth, height = img.size\n\n# Looking for rebar - often found in concrete construction, but this looks like wood.\n# Let&apos;s zoom into various parts to be sure.\nregions = [\n    [0, 0, 1000, 500],   # Top half (ceiling/background)\n    [500, 0, 1000, 1000], # Bottom half (floor/bench)\n    [0, 0, 500, 500],    # Top left\n    [0, 500, 500, 1000],  # Top right\n]\n\n# Convert normalized to pixel coordinates\ndef norm_to_pixel(norm_coords, w, h):\n    return [norm_coords[0] * h / 1000, norm_coords[1] * w / 1000, norm_coords[2] * h / 1000, norm_coords[3] * w / 1000]\n\nfor i, reg in enumerate(regions):\n    p_reg = norm_to_pixel(reg, width, height)\n    crop = img.crop((p_reg[1], p_reg[0], p_reg[3], p_reg[2]))\n    crop.save(f&apos;zoom_{i}.jpeg&apos;)\n&quot;,
    &quot;# Zoom into the background more clearly\nregions_bg = [\n    [350, 600, 600, 900], # Right background\n    [350, 300, 600, 600], # Center background\n    [350, 0, 600, 300],   # Left background\n]\n\nfor i, reg in enumerate(regions_bg):\n    p_reg = norm_to_pixel(reg, width, height)\n    crop = img.crop((p_reg[1], p_reg[0], p_reg[3], p_reg[2]))\n    crop.save(f&apos;bg_zoom_{i}.jpeg&apos;)\n&quot;
  ],
  &quot;images_generated&quot;: [
    {
      &quot;mime_type&quot;: &quot;image/jpeg&quot;,
      &quot;data&quot;: &quot;/9j/4AAQSkZJRgABAQAAAQABAAD... &amp;lt;truncated, 24532 chars&amp;gt;&quot;
    },
    {
      &quot;mime_type&quot;: &quot;image/jpeg&quot;,
      &quot;data&quot;: &quot;/9j/4AAQSkZJRgABAQAAAQABAAD... &amp;lt;truncated, 21176 chars&amp;gt;&quot;
    },
    {
      &quot;mime_type&quot;: &quot;image/jpeg&quot;,
      &quot;data&quot;: &quot;/9j/4AAQSkZJRgABAQAAAQABAAD... &amp;lt;truncated, 44788 chars&amp;gt;&quot;
    },
    {
      &quot;mime_type&quot;: &quot;image/jpeg&quot;,
      &quot;data&quot;: &quot;/9j/4AAQSkZJRgABAQAAAQABAAD... &amp;lt;truncated, 46680 chars&amp;gt;&quot;
    },
    {
      &quot;mime_type&quot;: &quot;image/jpeg&quot;,
      &quot;data&quot;: &quot;/9j/4AAQSkZJRgABAQAAAQABAAD... &amp;lt;truncated, 7760 chars&amp;gt;&quot;
    },
    {
      &quot;mime_type&quot;: &quot;image/jpeg&quot;,
      &quot;data&quot;: &quot;/9j/4AAQSkZJRgABAQAAAQABAAD... &amp;lt;truncated, 7332 chars&amp;gt;&quot;
    },
    {
      &quot;mime_type&quot;: &quot;image/jpeg&quot;,
      &quot;data&quot;: &quot;/9j/4AAQSkZJRgABAQAAAQABAAD... &amp;lt;truncated, 8192 chars&amp;gt;&quot;
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;code_executed&lt;/code&gt; array contains the Python scripts the model wrote and ran. The &lt;code&gt;images_generated&lt;/code&gt; array contains the base64-encoded JPEGs of each cropped region (7 images totaling ~160KB).&lt;/p&gt;
</content:encoded></item><item><title>The CLAUDE.md Said &apos;Never Use npm.&apos; Claude Used npm Anyway.</title><link>https://www.augusteo.com/blog/claude-ignored-my-instructions/</link><guid isPermaLink="true">https://www.augusteo.com/blog/claude-ignored-my-instructions/</guid><description>AI coding tools can ignore explicit project instructions under pressure, which is why human oversight and feedback loops still matter.</description><pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I watched Claude Code switch to npm in my Dockerfile. The CLAUDE.md file said &quot;Always use Bun instead of npm or yarn.&quot; Claude did it anyway.&lt;/p&gt;
&lt;p&gt;This isn&apos;t a hit piece. I use Claude daily. It&apos;s production-ready and writes good code. But this incident is a perfect example of why you can&apos;t just let AI coding tools rip unsupervised.&lt;/p&gt;
&lt;h2&gt;What Happened&lt;/h2&gt;
&lt;p&gt;I&apos;m working on a Bun-native project for fun. The Docker container wouldn&apos;t start. Claude diagnosed the issue: Bun doesn&apos;t fully support &lt;code&gt;renderToPipeableStream&lt;/code&gt;, a Node.js-specific API.&lt;/p&gt;
&lt;p&gt;Then it proposed a &quot;solution&quot;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;⏺ There&apos;s a Bun runtime compatibility issue with React&apos;s server-side rendering.
   Bun doesn&apos;t fully support renderToPipeableStream.
   The solution is to use Bun for fast installs/builds but Node.js for
   the production runtime. Let me fix this.

⏺ Write(Dockerfile)
  ⎿  Added 6 lines, removed 5 lines
      23 +# Stage 3: Production image with Node.js
      24 +# (React Router SSR requires Node.js stream APIs not fully supported by Bun)
      25 +FROM node:20-alpine AS release
      ...
      31 +CMD [&quot;npm&quot;, &quot;run&quot;, &quot;start&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Claude swapped my Bun production image for Node.js. Changed &lt;code&gt;bun run start&lt;/code&gt; to &lt;code&gt;npm run start&lt;/code&gt;. The CLAUDE.md file explicitly said never do this. It did it anyway.&lt;/p&gt;
&lt;h2&gt;The Catch&lt;/h2&gt;
&lt;p&gt;I caught it immediately. Hit Escape before it could rebuild the image.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;❯ NO! Never use npm, you need to remember this update Claude.md.
  Never, never, ever, ever use npm. If you encounter any issue,
  you need to fix it with Bun. Do a research into how to fix this with Bun.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This forced Claude to research instead of taking the shortcut.&lt;/p&gt;
&lt;h2&gt;The Correct Fix&lt;/h2&gt;
&lt;p&gt;After researching, Claude found the real solution: use &lt;code&gt;renderToReadableStream&lt;/code&gt; instead of &lt;code&gt;renderToPipeableStream&lt;/code&gt;. The first is a web standard. The second is Node-specific.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;⏺ Write(app/entry.server.tsx)
  ⎿  Wrote 57 lines to app/entry.server.tsx
      import { renderToReadableStream } from &quot;react-dom/server&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Dockerfile stayed pure Bun. No Node fallback needed.&lt;/p&gt;
&lt;p&gt;Claude&apos;s quick fix would have worked. The app would have run. But it was the wrong solution. The correct fix required research, and I had to push for it.&lt;/p&gt;
&lt;h2&gt;The Lucky Part&lt;/h2&gt;
&lt;p&gt;I don&apos;t always watch Claude work. Sometimes I kick off a task and check back later. This time I happened to be watching.&lt;/p&gt;
&lt;p&gt;If I hadn&apos;t been? That npm Dockerfile ships to production.&lt;/p&gt;
&lt;p&gt;The cost of that mistake: Bun installs are 20-40x faster than npm. It handles ~100k requests/second versus Node&apos;s ~25-30k. Teams report 35% lower serverless costs.&lt;/p&gt;
&lt;p&gt;I chose Bun for a reason. Claude almost tossed those benefits to solve a problem faster.&lt;/p&gt;
&lt;h2&gt;Why This Happens&lt;/h2&gt;
&lt;p&gt;Classic context rot. The instruction &quot;always use Bun&quot; sits at the top of CLAUDE.md. But then there&apos;s a long troubleshooting session with errors and logs and attempts. The immediate problem (make the Docker container work) becomes more salient than the standing rule (use Bun, not npm).&lt;/p&gt;
&lt;p&gt;Plus, npm is everywhere in Claude&apos;s training data. When it hits a wall, it defaults to what&apos;s familiar. Bun is newer. The muscle memory isn&apos;t there yet.&lt;/p&gt;
&lt;p&gt;This is why fully autonomous workflows like &lt;a href=&quot;https://awesomeclaude.ai/ralph-wiggum&quot;&gt;Ralph Wiggum&lt;/a&gt; make me skeptical. It&apos;s a technique where Claude loops until it declares &quot;DONE.&quot; No human in the loop. They work great until they don&apos;t. And when they don&apos;t, the wrong solution ships.&lt;/p&gt;
&lt;h2&gt;The Takeaway&lt;/h2&gt;
&lt;p&gt;Claude Code is ready for production work. I&apos;m not arguing otherwise. But the human in the loop isn&apos;t optional yet.&lt;/p&gt;
&lt;p&gt;When I caught this mistake, three things happened:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I interrupted before damage was done&lt;/li&gt;
&lt;li&gt;Claude researched and found the correct solution&lt;/li&gt;
&lt;li&gt;We updated CLAUDE.md with stronger language&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That feedback loop matters. Over time, you&apos;re training your AI collaborator. The instructions get more specific. The guardrails get tighter. Claude learns your project&apos;s rules.&lt;/p&gt;
&lt;p&gt;But it only works if you&apos;re watching.&lt;/p&gt;
&lt;p&gt;If you&apos;re adopting AI coding tools, my advice: stay engaged. You don&apos;t have to watch every keystroke. But check the diffs before they ship. Read what it changed. When it takes a shortcut you don&apos;t like, stop it and make it find the right solution.&lt;/p&gt;
&lt;p&gt;The AI is good. Really good. But &quot;good enough to ship without review&quot; isn&apos;t the same as &quot;good enough to trust completely.&quot;&lt;/p&gt;
</content:encoded></item><item><title>Estimation Isn&apos;t Impossible, Your Org Is Broken</title><link>https://www.augusteo.com/blog/estimation-is-impossible-broken-org/</link><guid isPermaLink="true">https://www.augusteo.com/blog/estimation-is-impossible-broken-org/</guid><description>Sean Goedecke&apos;s viral post on estimation describes a broken org, not reality. Here&apos;s what healthy estimation actually looks like.</description><pubDate>Sat, 24 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Sean Goedecke&apos;s post on &lt;a href=&quot;https://www.seangoedecke.com/how-i-estimate-work/&quot;&gt;how he estimates work&lt;/a&gt; went viral on Hacker News. I disagree with almost every premise it rests on.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Software engineering projects are not dominated by the known work, but by the unknown work, which always takes 90% of the time.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This isn&apos;t universally true. Every engineer knows what known work looks like. CRUD apps. Loading values from a database and displaying them in a table. Adding sorting and animations. Given a design, you can estimate these reliably.&lt;/p&gt;
&lt;p&gt;Novel work is different. Building a computer vision model that no one in the world has built before? That&apos;s going to have huge error bars. Maybe you can&apos;t estimate it at all.&lt;/p&gt;
&lt;p&gt;The conversation isn&apos;t about whether estimation is possible. It&apos;s about which category you&apos;re in and communicating that honestly.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Estimates are political tools for non-engineers in the organization.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I don&apos;t think calling it &quot;political&quot; is helpful. It makes everything sound adversarial.&lt;/p&gt;
&lt;p&gt;Product managers, sales teams, marketing teams need to know when they can promise a feature to users. They need to plan next quarter&apos;s work. They&apos;re relying on us engineers to set their expectations.&lt;/p&gt;
&lt;p&gt;When I transitioned into engineering leadership, I started seeing the other side more clearly. As &lt;a href=&quot;https://news.ycombinator.com/item?id=46742389&quot;&gt;notjustanymike&lt;/a&gt; put it in the Hacker News discussion:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;After owning a product, I&apos;ve developed a lot of sympathy for the people outside of engineering who have to put up with us... In a functioning org, there are lot of professionals depending on correct estimation to do their job.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Of course business and product people want everything as fast as possible. That&apos;s their job. They&apos;re trying to sell it and make money for the company. They&apos;re doing exactly what they should be doing.&lt;/p&gt;
&lt;p&gt;They&apos;re also operating in a high-ambiguity environment. They have their own stakeholders, their own deadlines, their own promises to keep.&lt;/p&gt;
&lt;p&gt;That&apos;s not politics. That&apos;s planning. Engineering is one of the most expensive investments a company makes. Estimation helps plan that investment.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;My job is to figure out the set of software approaches that match that estimate.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is backwards. Requirements define work, not the other way around.&lt;/p&gt;
&lt;p&gt;You understand the PRD. You get example inputs and outputs from the product team. You figure out whether this requires semantic search, highlighting, robust sourcing, or a mix of local and internet search. Then you estimate.&lt;/p&gt;
&lt;p&gt;I always ask the product team: give me an example of input and output. By looking at that, I know what system I need to build.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;The estimate doesn&apos;t come from the engineers. It comes from management.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This makes no sense. Estimates come from engineers. They&apos;re just not for engineers.&lt;/p&gt;
&lt;p&gt;But a lot of engineers don&apos;t feel empowered to push back. They don&apos;t feel they can say &quot;there&apos;s no way we can deliver what you want in the time you want.&quot; They think they just have to accept the timeline and figure it out.&lt;/p&gt;
&lt;p&gt;That&apos;s wrong. Our job is to communicate our limitations. We can&apos;t break the laws of physics. If you want it faster, something has to give. Quality, scope, or both.&lt;/p&gt;
&lt;p&gt;When a stakeholder asks &quot;Why will this take a month when that similar thing only took a week?&quot; that&apos;s not politics. That&apos;s an opportunity for education.&lt;/p&gt;
&lt;p&gt;You break it down: &quot;For this to work by that date, we need a certain number of annotations, a certain number of GPUs, and we need to get lucky that the architecture we pick works on the first try. If it doesn&apos;t, we try another architecture. We might need to re-annotate the training data.&quot;&lt;/p&gt;
&lt;p&gt;Now they understand. They can adjust their communication to customers accordingly.&lt;/p&gt;
&lt;p&gt;One useful framing comes from &lt;a href=&quot;https://news.ycombinator.com/item?id=46742389&quot;&gt;moribvndvs&lt;/a&gt; in the same Hacker News thread. Offer &quot;trim lines&quot; like a car:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;I&apos;ll put together 3 &apos;trim line&apos; options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Economy - bare functionality, fast delivery over reliability&lt;/li&gt;
&lt;li&gt;Mid tier - good enough quality, reliable, no frills&lt;/li&gt;
&lt;li&gt;Luxury - all the bells and whistles&quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;That&apos;s a conversation about trade-offs. You&apos;re working together to refine the requirements until they fit the timeline, or adjust the timeline to fit the requirements.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Step 1: Extract the political context... What does management need? What kind of pressure are they under?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We&apos;re on the same team. Imagine a basketball team where the point guard doesn&apos;t understand what the center is doing. That makes no sense.&lt;/p&gt;
&lt;p&gt;Engineers need to learn what the product manager needs. What sales needs. What the CEO needs. And those stakeholders need to learn how to think about engineering work. You can&apos;t stay in silos.&lt;/p&gt;
&lt;p&gt;I&apos;ve worked at Apple and at a startup that had 7 engineers when I joined and now has 30. The estimation process is almost identical. A program manager (or at a startup, the CEO or PM lead) comes to the engineering lead and asks how long something will take. The lead works with the engineers to estimate. Priorities get tracked.&lt;/p&gt;
&lt;p&gt;The only difference is startups don&apos;t have program managers. The fundamentals are the same.&lt;/p&gt;
&lt;h2&gt;The heuristics work well enough&lt;/h2&gt;
&lt;p&gt;Once you&apos;re having real conversations, the actual mechanics of estimation are straightforward.&lt;/p&gt;
&lt;p&gt;We use the &quot;multiply by two, add 20%&quot; rule because we don&apos;t know what we don&apos;t know. It&apos;s a rough correction for optimism bias.&lt;/p&gt;
&lt;p&gt;As you get more senior, you calibrate based on who&apos;s doing the work. &quot;I can trust this engineer, she&apos;s really fast.&quot; &quot;This engineer is thorough but slower.&quot; You factor that in.&lt;/p&gt;
&lt;p&gt;None of this is political. It&apos;s just experience.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The article resonated with a lot of people, which tells me a lot of engineers work in organizations where estimation does feel adversarial. If that&apos;s your situation, I&apos;m sorry.&lt;/p&gt;
&lt;p&gt;But that&apos;s not estimation being impossible. That&apos;s communication being broken.&lt;/p&gt;
&lt;p&gt;If we assume good will from everyone, this is a solvable problem. Business people need to understand that engineering is trying their best. Engineers need to understand that business people aren&apos;t the enemy.&lt;/p&gt;
&lt;p&gt;Of course, if you have bad actors and assholes, that&apos;s another story entirely. But that&apos;s a people problem, not an estimation problem.&lt;/p&gt;
&lt;p&gt;Estimation is hard. Not impossible. Talk to each other.&lt;/p&gt;
</content:encoded></item><item><title>Turn On Your Camera</title><link>https://www.augusteo.com/blog/turn-on-your-camera/</link><guid isPermaLink="true">https://www.augusteo.com/blog/turn-on-your-camera/</guid><description>Keeping your camera on in remote meetings builds trust, helps people remember you, and signals engagement, and the benefits far outweigh the minor fatigue cost.</description><pubDate>Sat, 06 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve been working remotely for years now, and there&apos;s one thing I keep noticing: fewer and fewer people turn on their cameras during video calls. And I get it. Zoom fatigue is real. But here&apos;s the thing: &lt;strong&gt;the benefits of having your camera on far outweigh the costs.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This isn&apos;t about looking polished or having a nice background. It&apos;s about building trust, staying accountable, and actually connecting with your teammates in an environment where we&apos;re already operating at lower bandwidth than in-person work.&lt;/p&gt;
&lt;p&gt;Let me break this down.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Benefits for You&lt;/h2&gt;
&lt;p&gt;When you turn on your camera, people remember you. It&apos;s just human nature.&lt;/p&gt;
&lt;p&gt;Think about a meeting with 10 people where only 5 have their cameras on. Your eyes naturally go to those 5 faces. You build a mental image of who they are. The other 5? They&apos;re technically there, but you don&apos;t feel their presence the same way.&lt;/p&gt;
&lt;p&gt;If you&apos;re a junior employee trying to make an impression, this matters a lot. Leaders notice who shows up. Not just who attends the meeting, but who shows up.&lt;/p&gt;
&lt;p&gt;Having your camera on also gives context. When I&apos;m driving and can&apos;t fully participate, I still hold my phone up so people can see I&apos;m in the car. Now they understand where I am. They set expectations accordingly. It builds another layer of trust.&lt;/p&gt;
&lt;p&gt;This matters because remote work doesn&apos;t have the &quot;water cooler chatter&quot; of an office. We don&apos;t bump into each other in hallways. We don&apos;t grab coffee together. The camera is one of the few ways we can share a bit of who we are beyond just our voices.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Benefits for Your Teammates&lt;/h2&gt;
&lt;p&gt;Communication over video is already limited compared to being in person. When you add your face to the call, you&apos;re giving your teammates more information to work with.&lt;/p&gt;
&lt;p&gt;They can see you nod in agreement. They can catch you furrowing your brow in confusion. They can tell when you&apos;re engaged versus when you&apos;re zoning out. These cues matter. &lt;strong&gt;Non-verbal communication makes up a huge part of how humans understand each other.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It also makes meetings less awkward. There&apos;s nothing worse than presenting to a wall of black rectangles. Some of my coworkers call this &quot;talking to the void.&quot; When people have their cameras off, the speaker has no feedback. No reactions. Nothing. It&apos;s honestly kind of depressing.&lt;/p&gt;
&lt;p&gt;When you turn on your camera, you&apos;re showing respect for the people presenting. You&apos;re signaling that this meeting matters, that their time matters, that you&apos;re actually there with them.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Downsides of Keeping Your Camera Off&lt;/h2&gt;
&lt;p&gt;We&apos;ve all been in that meeting. Someone has their camera off. Their name gets called. Silence. Then after a few seconds: &quot;Hey, can you repeat the question please?&quot;&lt;/p&gt;
&lt;p&gt;It&apos;s obvious they were doing something else. And look, I understand that multitasking is hard to avoid in modern work. But when your camera is off, it&apos;s way easier to drift. When your camera is on, your brain treats the meeting differently. You&apos;re more present. You&apos;re more focused.&lt;/p&gt;
&lt;p&gt;Research backs this up too. People who keep cameras off are often perceived as less engaged, less trustworthy, and less suited for leadership roles. Fair or not, &lt;strong&gt;your teammates and your managers notice.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some findings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;a href=&quot;https://www.entrepreneur.com/leadership/the-pros-and-cons-of-cameras-on-during-virtual-meetings/450959&quot;&gt;Vyopta survey&lt;/a&gt; found that 92% of executives think workers who turn cameras off don&apos;t have a long-term future at the company.&lt;/li&gt;
&lt;li&gt;A &lt;a href=&quot;https://www.kornferry.com/about-us/press/cameras-on-or-off&quot;&gt;Korn Ferry survey&lt;/a&gt; found that 76% of professionals believe people who leave cameras off are looked upon negatively, and 60% say it&apos;s a &quot;career minimizing move.&quot;&lt;/li&gt;
&lt;li&gt;Research published in the &lt;a href=&quot;https://onlinelibrary.wiley.com/doi/full/10.1002/ejsp.3167&quot;&gt;European Journal of Social Psychology&lt;/a&gt; found that turning your camera off results in being perceived as less engaged, less moral, and less suitable for leadership.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can either accept human nature or fight against it. But you&apos;re probably going to lose if you try to fight it.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;But What About Zoom Fatigue?&lt;/h2&gt;
&lt;p&gt;I hear this one a lot. &quot;Cameras are exhausting.&quot;&lt;/p&gt;
&lt;p&gt;And yes, there&apos;s research showing that being on camera can increase fatigue, I&apos;m not dismissing that.&lt;/p&gt;
&lt;p&gt;But here&apos;s my take: everything in life is a cost-benefit analysis.&lt;/p&gt;
&lt;p&gt;Sure, being on camera takes energy. But the trade-off is that your teammates trust you more. Your managers remember you. You build relationships. You stay focused. Those benefits are significant.&lt;/p&gt;
&lt;p&gt;If you&apos;re worried about how you look, don&apos;t be. I&apos;ve been in meetings in my pajamas plenty of times even at Apple. Let alone at startup. Nobody cares. People sometimes say it&apos;s cute, but it&apos;s genuinely not an issue. You&apos;re not in an interview anymore. You&apos;re not trying to impress people with your appearance. You&apos;re trying to build something together.&lt;/p&gt;
&lt;p&gt;If you&apos;re worried about your living space, use a virtual background. Problem solved.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;My Personal Rule&lt;/h2&gt;
&lt;p&gt;I turn my camera on about &lt;strong&gt;95% of the time&lt;/strong&gt;. The only times I don&apos;t are when I physically can&apos;t, like when I&apos;m driving through a tricky area where Tesla FSD isn&apos;t handling things well and I need both hands on the wheel.&lt;/p&gt;
&lt;p&gt;Even when I&apos;m driving on easier roads, I&apos;ll hold my phone to show my face so people have context. They can see I&apos;m in transit. They understand the situation.&lt;/p&gt;
&lt;p&gt;I&apos;d advocate the same for everyone working remotely. If you want to build relationships, if you want people to trust you, if you want to be remembered, just turn it on. It&apos;s fine. Really.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;For Leaders&lt;/h2&gt;
&lt;p&gt;If you&apos;re a manager or team lead, this applies to you too. Maybe even more so.&lt;/p&gt;
&lt;p&gt;You set the example. If you have your camera on, your team is more likely to follow. If you&apos;re always camera-off, don&apos;t be surprised when everyone else does the same.&lt;/p&gt;
&lt;p&gt;Advocate for cameras-on culture in your team. Not as a rigid mandate, but as a norm. Explain why it matters. Make it clear that nobody&apos;s judging appearances or backgrounds. What matters is that you&apos;re present with each other.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;Remote work is already lower bandwidth than being in person. We lose the hallway conversations, the lunch chats, the accidental run-ins that build relationships over time.&lt;/p&gt;
&lt;p&gt;The camera is one of the few tools we have to close that gap. It lets people see your face, read your expressions, and feel like they&apos;re actually talking to a person rather than a name on a screen.&lt;/p&gt;
&lt;p&gt;If you don&apos;t care about your career, if you&apos;re already looking for another job, sure, keep your camera off. But if you want better relationships with your teammates, if you want your leaders to remember you, if you want to actually be present in your work, just turn it on.&lt;/p&gt;
&lt;p&gt;It&apos;s not about looking perfect. It&apos;s about showing up.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;My rule: 95% cameras-on. The other 5% is when I literally can&apos;t. Try it for a month and see if your meetings feel different.&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>El Salvador with Kids: 5 Days of Adventure, Vomit, and Unexpected Familiarity</title><link>https://www.augusteo.com/blog/el-salvador-family-trip-2025/</link><guid isPermaLink="true">https://www.augusteo.com/blog/el-salvador-family-trip-2025/</guid><description>Our family of four spends 5 days in El Salvador on a budget, navigating rental car chaos, armed security everywhere, two vomiting kids, and discovers a country in the middle of a post-gang renaissance that feels surprisingly like Indonesia.</description><pubDate>Sat, 29 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;It started the way a lot of our family trips do: opening Google Flights during the kids&apos; Thanksgiving break, looking for somewhere warm and cheap. Living in California, we wanted to go south, no snow, no cold. Mexico and most of South America were quoting us $3,500-4,500 for four people. El Salvador? $1,896 return.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-map.jpg&quot; alt=&quot;el-salvador-family-trip-2025-map.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;That caught my attention. I remembered reading about Bukele&apos;s crackdown on MS-13, how the country had transformed from one of the most dangerous places in the Western Hemisphere to reportedly safer than Mexico. Did some research, confirmed it seemed legit, and booked it. Total planning time: maybe two weeks, mostly using Claude and ChatGPT to batch out activities by region rather than a rigid hour-by-hour itinerary.&lt;/p&gt;
&lt;p&gt;What we got was five days that felt less like Central America and more like a time machine back to Indonesia.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Day 1: The Airport Adventure Begins&lt;/h2&gt;
&lt;p&gt;We landed around 6-7am after a red-eye from San Jose. The airport was surprisingly modern, we even took the obligatory photo with the country seal. And then things got interesting.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-E4A780D1-71F0-4950-905F-D99C940EA756.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-E4A780D1-71F0-4950-905F-D99C940EA756.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8312.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8312.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I&apos;d booked with Sixt because they were half the price of everyone else. Couldn&apos;t find them anywhere in the airport car rental area. Using Google Translate, we talked to an officer who motioned for us to follow her. She grabbed a taxi driver, and suddenly we were walking through dirt paths outside the airport, following this guy to his car parked in some random lot, then driving to an unknown destination.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8320.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8320.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;He was on WhatsApp the whole time, calling people, clearly trying to figure out where this place actually was. I pulled up Google Maps and showed him the address from my booking confirmation. He looked confused. We turned onto a rocky road that was clearly still under construction, and there it was: a tiny office in the middle of nowhere with maybe three cars parked outside.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8322.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8322.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Lesson learned: don&apos;t cheap out on car rentals in El Salvador. But also, it&apos;s an adventure. I had the same sketchy experience returning the car later. Just part of the charm.&lt;/p&gt;
&lt;p&gt;The drive into San Salvador proper was my first &quot;wait, is this Indonesia?&quot; moment. The outskirts looked exactly like Indonesian villages, people riding on the open backs of pickup trucks, no safety equipment, just holding onto the frame. Coconut sellers everywhere. The chaos felt familiar.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-01f82546-a14f-4ad0-9fdf-81582c43d9d9.png&quot; alt=&quot;el-salvador-family-trip-2025-01f82546-a14f-4ad0-9fdf-81582c43d9d9.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Can’t figure out how to embed videos, so here’s a screenshot&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_1757.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_1757.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_1810_6.21.55_PM.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_1810_6.21.55_PM.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We dropped luggage at the hotel (couldn&apos;t check in yet at 9am) and headed to MultiPlaza mall to get an eSIM. $15 for 12GB at the Claro shop. The signup was easy, but slow. Everyone here moves slowly. Very friendly, but slow. Just like Indonesia.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_1771.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_1771.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_1825.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_1825.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The mall had tons of kid stuff for Christmas, and we ended up spending way longer than planned because Chuck E. Cheese had a 30-minute unlimited play deal for $16. The trick is that there&apos;s a short cooldown between scans, so you just wait a few seconds and scan again. The kids racked up 471 tickets, then the live Chuck E. Cheese mascot came out, did a dance with them, and threw bonus tickets everywhere. Grace and George scrambled to collect another 65. Good times.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8342.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8342.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-261E80D9-767F-4951-80BD-1D9DAAC54E74.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-261E80D9-767F-4951-80BD-1D9DAAC54E74.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;That evening we went to Picnic, this outdoor restaurant complex with moving animal statues, a massive fountain, and their famous rainbow slide. I&apos;d give the slide 4 out of 5 stars, genuinely fun. They had a VR walking attraction that would&apos;ve been 4.5 stars if the staff could explain it in English, but they only knew words like &quot;fish&quot; and &quot;planets&quot; and &quot;you.&quot; The kids loved it anyway.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_1840.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_1840.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_1914.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_1914.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Dinner was a feast of seafood and steak for $58 total. Four people, stuffed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8383.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8383.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8386.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8386.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Grace twisted her ankle near the fountain due to uneven ground. First casualty of the trip. We rode a carousel that inexplicably spoke Chinese (clearly imported directly from China, language settings untouched), walked through a Jurassic Park dinosaur exhibit, then hit up a Whole Foods-type supermarket. Fun fact: there&apos;s basically one brand of bottled water here called Cristal. Complete monopoly.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_1907.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_1907.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The city is beautiful at night. Light shows everywhere.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Day 2: City Tour and the Vomit Begins&lt;/h2&gt;
&lt;p&gt;Originally planned to hike a volcano, but everyone was exhausted from the red-eye. We pivoted to a city tour instead.&lt;/p&gt;
&lt;p&gt;Started at the Mercado Nacional. Rows of stalls selling identical stuff: knick-knacks, knives, fridge magnets, Bukele figurines in various costumes. His face is everywhere here, along with the First Lady. Either people genuinely love him or it&apos;s mandatory. Based on the fridge magnets and mini figurines, I think they actually love him.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-07510F50-603B-477A-B852-59D1712AF572.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-07510F50-603B-477A-B852-59D1712AF572.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-520C96E6-5FA4-4A17-969A-6ABCCCFEFE0A.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-520C96E6-5FA4-4A17-969A-6ABCCCFEFE0A.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-5c2ff008-c160-40f3-aa6f-a215afe98aca.png&quot; alt=&quot;el-salvador-family-trip-2025-5c2ff008-c160-40f3-aa6f-a215afe98aca.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8426.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8426.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8428.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8428.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The historical district was chaos. So many roadblocks and one-way streets that it took forever to find parking. We eventually found a spot near a traditional market that was definitely not touristy. Loud, dirty, real.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-c173bf74-886e-44cf-a45e-98bda319d603.png&quot; alt=&quot;el-salvador-family-trip-2025-c173bf74-886e-44cf-a45e-98bda319d603.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-010f739e-14fe-48bf-9bdb-9725ffaf139f.png&quot; alt=&quot;el-salvador-family-trip-2025-010f739e-14fe-48bf-9bdb-9725ffaf139f.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-c4b8051a-ea14-475e-935d-99beafb7ea81.png&quot; alt=&quot;el-salvador-family-trip-2025-c4b8051a-ea14-475e-935d-99beafb7ea81.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-471abe96-576e-4e62-8a57-0cb80429d0a2.png&quot; alt=&quot;el-salvador-family-trip-2025-471abe96-576e-4e62-8a57-0cb80429d0a2.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_1930.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_1930.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The highlight was BINAES, the National Library. Seven stories, and the kids went nuts. The first floor is for small children, upper floors have LEGO, video games, even VR. Kids can earn gaming credits by reading books: 3 minutes of reading for 10 minutes of game time. Terrible exchange rate, but good incentive design. They had Harry Potter, Lord of the Rings, and Game of Thrones promotional displays. Interesting franchise choices for a library.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-75dbb34b-481c-40d4-bf43-0c54798a4c5c.png&quot; alt=&quot;el-salvador-family-trip-2025-75dbb34b-481c-40d4-bf43-0c54798a4c5c.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8466.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8466.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-46752EBB-65BF-4260-A074-0FFC13553E86.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-46752EBB-65BF-4260-A074-0FFC13553E86.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-FA41E953-8840-4F2F-B7E9-113B76A58AA8.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-FA41E953-8840-4F2F-B7E9-113B76A58AA8.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_1938.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_1938.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We visited the rainbow stained-glass church next to Plaza Libertad. Beautiful, packed with locals. Everyone stared at us. Worth noting: we saw almost no other Asian tourists the entire trip. One group at this church, one family at hotel breakfast, one mom with daughters at the airport on the way out. That&apos;s it. We stood out.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_1998.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_1998.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-f0b7dded-f2db-41ea-b294-07855395e48f.png&quot; alt=&quot;el-salvador-family-trip-2025-f0b7dded-f2db-41ea-b294-07855395e48f.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-51403B48-0E35-49E5-9BCC-2950212ED99C.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-51403B48-0E35-49E5-9BCC-2950212ED99C.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The Teatro Nacional was worth the tour. Private guide, very cheap. Painted ceiling like Italian theaters, the president has his own exclusive box, and there&apos;s a 7-meter deep pool under the stage to absorb sound. Candice played piano in the recital room.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_2031.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_2031.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We also hit the Palacio Nacional, the old government building with color-coded rooms. Pink for Supreme Court, blue for parliament. The courtyard had a massive tree, great for photos.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-029592A2-E51C-44CA-B21D-7528D2D50709.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-029592A2-E51C-44CA-B21D-7528D2D50709.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-4D299AB7-EAE6-45F9-ACA1-4F33C4812F9B.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-4D299AB7-EAE6-45F9-ACA1-4F33C4812F9B.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8520.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8520.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;George needed to pee, so we went back to the library and ended up staying a while. They have beds and bean bags. The AC was glorious. Kids jumped around on bean bags while Candice and I caught our breath. On the walk back to the car, she bought a bag of rambutan from a street vendor for only $1. Cheap.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8485.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8485.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We tried Parque Cuscatlán at night because supposedly the lights are pretty. It was meh. No good food nearby either.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_2136.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_2136.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;That&apos;s when George started vomiting. First one caught us off guard, went straight onto the car seat. We stopped at the hotel, cleaned what we could with tissues, then drove to a pharmacy for baking soda. The lady there gave us a bunch of plastic bags too. Nice lady.&lt;/p&gt;
&lt;p&gt;Ate sushi at Bambú mall (ramen and California rolls), then back to the hotel where I covered the car seat in baking soda and cracked the windows. George vomited multiple times that night. No fever, just gastritis. Purely viral. Should be better tomorrow.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Day 3: Pyramids, Lakes, and More Vomit&lt;/h2&gt;
&lt;p&gt;Morning started with a car wash to clean the baking soda disaster. $20, about an hour. We walked to a pharmacy for hydrocortisone (Grace&apos;s ankle was swollen and bug-bitten), then waited at a mall while George lay down feeling miserable. Bought electrolytes.&lt;/p&gt;
&lt;p&gt;Once the car was ready, we drove to San Andrés ruins. Really pretty pyramids sitting on lush green fields. There was a school group visiting, some students interviewed Candice for what seemed like a class project. Asked her which part was her favorite. &quot;We don&apos;t know, we just arrived.&quot; They pointed us toward the pyramids.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_2213.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_2213.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It was brutally hot, but we found a tree in the middle of the field and rested in the shade. The coconut seller there was great, the kids brought their empty coconuts back and used a chopping hand gesture to ask her to split them open. She understood immediately. You&apos;d be surprised how much you can communicate with just body language.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_2212.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_2212.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-3032a932-7cf9-43fa-be65-1d4c2678467e.png&quot; alt=&quot;el-salvador-family-trip-2025-3032a932-7cf9-43fa-be65-1d4c2678467e.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The drive to Lake Coatepeque was surreal. Armed soldiers and police stationed along the entire route, all holding M4 assault rifles. Not just at the lake, I saw heavily armed security everywhere in El Salvador. Even the supermarket security guards carry shotguns. Long ones.&lt;/p&gt;
&lt;p&gt;At first it felt intimidating, but after watching documentaries about the MS-13 crackdown, I understood. These guys are defending against gangs who used to kill with impunity. Every interaction we had with police and soldiers was friendly. Smiles, waves. They could tell we were tourists.&lt;/p&gt;
&lt;p&gt;Lunch was at Rancho Alegre by the lake. Live DJ singing and playing music. There was a diving board off the first floor, a sad little playground, a dirty swimming pool with three slides (we skipped it), and a trampoline. Grace and George played on the trampoline with a local kid. Kids don&apos;t care about language barriers, they just figured it out.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8690.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8690.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8683.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8683.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We ordered rabbit, mixed grill, chicken, fish. $40 total. Insane value.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_2287.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_2287.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The drive back took almost two hours due to traffic and construction. Construction everywhere in this country, buildings going up, streets being widened, Christmas decorations being installed. El Salvador is clearly in the middle of a renaissance. Waze took us through back streets that looked exactly like poor neighborhoods in Indonesia: small houses with zinc roofs, people sitting outside chatting with neighbors. Pre-internet vibes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-8779a80a-b7e8-4e54-b007-31e450564619.png&quot; alt=&quot;el-salvador-family-trip-2025-8779a80a-b7e8-4e54-b007-31e450564619.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-c344711e-5e6d-4ebb-8866-e81115dfdf38.png&quot; alt=&quot;el-salvador-family-trip-2025-c344711e-5e6d-4ebb-8866-e81115dfdf38.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Candice drove for the first time this trip and handled it perfectly. The aggressive driving culture is similar to China, so she&apos;s no stranger to it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8729.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8729.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Back at the hotel, we swam, hit the jacuzzi, then ordered room service: chicken soup, spinach linguine, salmon burgers. Good day despite George vomiting into a plastic bag during the drive. At least it didn&apos;t hit the seats this time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-0467438F-C545-4416-A5CE-E7C7101B06F8.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-0467438F-C545-4416-A5CE-E7C7101B06F8.jpeg&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Day 4: Beach Day and the Google Maps Disaster&lt;/h2&gt;
&lt;p&gt;Grace&apos;s turn to be sick. Vomited after hotel breakfast, multiple times. We started late.&lt;/p&gt;
&lt;p&gt;Originally planned to hike to a waterfall, but after talking to another tourist (Zack from Miami) who said it took 3+ hours, we decided the kids weren&apos;t healthy enough. Pivoted to beach day instead.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8756.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8756.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_2320.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_2320.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Accidentally drove to La Libertad first instead of El Sunzal. Turned out to be a happy accident. La Libertad is more developed, proper restaurants, shops, even a run-down amusement park. We had lunch at a beachfront restaurant.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8769.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8769.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8773.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8773.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8771.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8771.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It was loud. Every restaurant blasting their own music, plus multiple mariachi bands competing to be heard. One band was mostly young kids and genuinely annoying. The others were more refined, slower songs, actually added to the atmosphere. We treat trips like this as novelty pumps. New experiences, even chaotic ones, are the point.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_2345.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_2345.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After lunch, we drove to El Sunzal and stopped for coconut along the way. George was struggling to scrape out the meat (too slow), so the lady just took over and fed him. He rolled with it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8785.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8785.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;El Sunzal had black sand beaches but was very undeveloped, reminded me of Pagar Timun in Ketapang. Bunch of food stalls, kids played in the sand, I had a fresca. Then back to the hotel.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8790.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8790.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_2396.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_2396.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_2362.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_2362.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_2358.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_2358.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Candice took the kids swimming while I returned the rental car. Forgot to fill up gas. Opened Google Maps to find the nearest station.&lt;/p&gt;
&lt;p&gt;Big mistake.&lt;/p&gt;
&lt;p&gt;Google Maps led me down increasingly sketchy roads, dirt, rocks, mud, trees everywhere, extremely narrow. Complete dead end. Locals were standing around staring at my car like &quot;who is this idiot?&quot; I had to reverse the entire way out. It was dark, no lights, completely alone. Every gang documentary I&apos;d watched flashed through my mind. If something happened here, there&apos;d be no recourse. I was genuinely relieved Candice and the kids were safe at the hotel pool.&lt;/p&gt;
&lt;p&gt;Switched to Waze. The real road still didn&apos;t match the map perfectly, but I made it to the gas station. Lesson learned: always check which navigation app locals use. Waze works here. Google Maps will get you killed (okay, maybe not killed, but definitely lost in sketchy forests).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8806.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8806.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Returned the car successfully. Amazing. Took an Uber back to the hotel for $1.68.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8807.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8807.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Dinner at the hotel restaurant, good spaghetti and pupusas. Played pool with the kids until 10:30pm. It was Grace and George&apos;s first time ever playing pool, just messing around, learning the basics. Then packing, then sleep.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8811.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8811.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-8FF62807-A065-4F84-88F5-9433B926F880.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-8FF62807-A065-4F84-88F5-9433B926F880.jpeg&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Day 5: The Fly Back (Barely)&lt;/h2&gt;
&lt;p&gt;4:30am wake-up call. We stayed at the Quality Hotel near the airport (yes, that&apos;s literally the name) and grabbed breakfast at 5am. Solid hotel breakfast, no complaints.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-BDFB1031-DA1A-47D5-B3D5-68647A41409C.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-BDFB1031-DA1A-47D5-B3D5-68647A41409C.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The airport was chaos.&lt;/p&gt;
&lt;p&gt;The Avianca check-in line was absolutely packed. Looking at the queue, there was no mathematical way we&apos;d make our flight. Luckily, they started calling out passengers flying to Miami or San Francisco to come to the front. We got through check-in with maybe 5 minutes before boarding. Cutting it way too close.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-7E986F28-B1A9-45B0-97D4-AF6D4562D779.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-7E986F28-B1A9-45B0-97D4-AF6D4562D779.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;What surprised me was how strict security was compared to TSA at SFO. Back home, we don&apos;t even have to take off our shoes anymore. Here? Shoes off. They also caught my nail clipper buried in my backpack. TSA never flagged it on the way down. The security guy pulled it out, showed it to his supervisor, who eventually waved it through. But that little delay when you&apos;re already running late? Not fun.&lt;/p&gt;
&lt;p&gt;Then there&apos;s a second security checkpoint right at the boarding gate, Singapore-style. Once you&apos;re through, you&apos;re trapped. No bathroom access, no water (they make you pour it out), no leaving the holding area. Plan accordingly.&lt;/p&gt;
&lt;p&gt;The Avianca flight itself: cheap for a reason. This was the first flight I&apos;ve ever been on where the seats don&apos;t recline at all. Not &quot;limited recline.&quot; Zero recline. Completely vertical for 6 hours. No complimentary food but they gave us a bottle of water. Everything else costs extra. Pure budget airline experience.&lt;/p&gt;
&lt;p&gt;Here’s a kid sleeping on the floor.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-IMG_8838.jpeg&quot; alt=&quot;el-salvador-family-trip-2025-IMG_8838.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Thank god I brought my turtle travel pillow. Without it, sleeping would&apos;ve been nearly impossible. The kids managed okay, probably because they can sleep anywhere. Candice and I just endured.&lt;/p&gt;
&lt;p&gt;Landed at SFO, took a taxi home. Adventure complete.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;On the Indonesia parallels:&lt;/strong&gt; This was the biggest surprise. The pace of life, the driving chaos, the buses that stop anywhere to pick up passengers (they have &quot;kernets&quot; - guys hanging off the bus shouting destinations), and “bajaj”, the stratification between wealthy city centers and poor outskirts, the zinc-roof houses. It all felt familiar in a way I didn&apos;t expect from Central America.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-image.png&quot; alt=&quot;el-salvador-family-trip-2025-image.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Indonesian Kernet&lt;/p&gt;
&lt;p&gt;![el-salvador-family-trip-2025-image 1.png](https://www.augusteo.com/assets/blog/el-salvador-family-trip-2025/el-salvador-family-trip-2025-image 1.png)&lt;/p&gt;
&lt;p&gt;Indonesian Bajaj&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On safety:&lt;/strong&gt; Yes, there are armed soldiers everywhere. Yes, it felt surreal at first. But every interaction was friendly. The Bukele crackdown seems to have genuinely transformed the country. Construction everywhere suggests people finally feel safe enough to invest and build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On food:&lt;/strong&gt; Everything was cheap and good. Pupusa quality varies by chef, and the hotel restaurant ones were great. We avoided street food to prevent stomach issues (ironic, since both kids got gastritis anyway from who knows what). Horchata was fine, nothing special. Basically sweet rice drink. The mixed grills and seafood were the highlights.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On tourism infrastructure:&lt;/strong&gt; This is still a raw place. Not ready for mass tourism. The rental car situation, the navigation app problems, the language barrier (almost no one speaks English), the lack of signage. It&apos;s all very DIY. Only come here if you want authentic adventure, not polished resort experiences.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On the people:&lt;/strong&gt; Universally friendly and helpful. Even when communication was impossible, body language and Google Translate got us through. Don&apos;t let them type on your phone though. Some of the older folks type one letter at a time. As fast as Flash the DMV sloth from Zootopia.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On Bitcoin:&lt;/strong&gt; Completely invisible. We never saw a single Bitcoin ATM or sign accepting crypto. Just credit cards and cash.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Would we go back?&lt;/strong&gt; Yes, but not soon. When Grace and George are teenagers with stronger immune systems, we&apos;ll return for the volcano hike and waterfall we missed. The nature here is stunning and still pristine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Three words for El Salvador:&lt;/strong&gt; Raw, cheap, surprisingly familiar.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Trip stats: 5 days, 2 vomiting children, 1 Google Maps near-disaster, 1 nail clipper interrogation, 0 MS-13 encounters, 0 degrees of seat recline on Avianca, pupusas everywhere, and countless good food and drinks.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;PS: All the juices are real and tasted so good.&lt;/p&gt;
</content:encoded></item><item><title>Why Your Fear of Looking Stupid Is Your Biggest Career Risk</title><link>https://www.augusteo.com/blog/moat-of-low-status-middle-status-trap/</link><guid isPermaLink="true">https://www.augusteo.com/blog/moat-of-low-status-middle-status-trap/</guid><description>The people most afraid to look stupid - those with middle status - are trapped in a psychological prison that prevents them from learning and adapting, while both high-status and low-status individuals paradoxically have the freedom to embrace being beginners and thus learn faster in our rapidly changing world.</description><pubDate>Sun, 06 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;So I was scrolling through &lt;a href=&quot;https://news.ycombinator.com/item?id=44444686&quot;&gt;Hacker News yesterday&lt;/a&gt; and stumbled upon this discussion about Cate Hall&apos;s concept of the &lt;a href=&quot;https://usefulfictions.substack.com/p/learn-to-love-the-moat-of-low-status&quot;&gt;&quot;Moat of Low Status&quot;&lt;/a&gt;. The idea hit me like a ton of bricks - not because it&apos;s revolutionary (people have been saying &quot;embrace being a beginner&quot; forever), but because of how &lt;em&gt;urgent&lt;/em&gt; this mindset has become in 2025.&lt;/p&gt;
&lt;p&gt;Here&apos;s the thing: the world is moving so fast now that whatever you&apos;re comfortable doing today will probably be obsolete in a few years. Maybe even months. &lt;strong&gt;The only sustainable advantage is getting really, really good at being bad at things.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;The Status Paradox&lt;/h2&gt;
&lt;p&gt;But here&apos;s what really struck me from the discussion - the moat of low status isn&apos;t equally hard for everyone to cross. There&apos;s a cruel paradox at play here.&lt;/p&gt;
&lt;p&gt;If you&apos;re &lt;strong&gt;very high status&lt;/strong&gt;, you can afford to look stupid. People assume you&apos;re asking &quot;dumb&quot; questions strategically. You have enough social capital banked that a few withdrawals won&apos;t hurt. When a senior executive asks basic questions, people think &quot;they&apos;re getting back to first principles&quot; or &quot;they&apos;re testing us.&quot;&lt;/p&gt;
&lt;p&gt;If you&apos;re &lt;strong&gt;very low status&lt;/strong&gt;, they also have nothing to lose. If you learned to always assume that they were the dumbest person in the room. This became a superpower - they could ask whatever they wanted because they had no status to protect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But if you&apos;re in the middle?&lt;/strong&gt; That&apos;s where the moat becomes a prison. You have just enough status to lose, but not enough to waste. You&apos;re trapped in what researchers call a &lt;a href=&quot;https://en.wikipedia.org/wiki/Social_trap&quot;&gt;social trap&lt;/a&gt; - where short-term social preservation leads to long-term stagnation.&lt;/p&gt;
&lt;p&gt;This aligns perfectly with &lt;a href=&quot;https://www.scientificamerican.com/article/the-psychology-of-social/&quot;&gt;psychological research on social status&lt;/a&gt;. Middle-status individuals are the most anxious about their position, the most likely to conform, and the least likely to take risks that might threaten their precarious perch.&lt;/p&gt;
&lt;h2&gt;The Dumb Questions Superpower&lt;/h2&gt;
&lt;p&gt;One comment that resonated with me was about a senior engineer who deliberately asks the &quot;dumb&quot; questions in meetings because they can afford the social hit. They&apos;re using their high status as a shield to help juniors who might be thinking the same thing but are too afraid to speak up.&lt;/p&gt;
&lt;p&gt;I&apos;ve been doing this for years, though not always intentionally. In meetings, I&apos;ll ask questions constantly - sometimes about things I already understand, sometimes about things I genuinely don&apos;t know. And yeah, sometimes I look like an idiot because I wasn&apos;t paying attention and it was already explained.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But here&apos;s what I&apos;ve learned: I&apos;d rather look dumb for 30 seconds than be ignorant forever.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;My kids hear this from me all the time. When they&apos;re struggling with something new, I remind them: &quot;You&apos;re bad at this &lt;em&gt;right now&lt;/em&gt;. Everyone is bad at things when they start. The only way to get better is to keep doing it.&quot;&lt;/p&gt;
&lt;h2&gt;The Beginner&apos;s Mind Is Not Optional&lt;/h2&gt;
&lt;p&gt;There&apos;s a concept in Zen Buddhism called &lt;a href=&quot;https://en.wikipedia.org/wiki/Shoshin&quot;&gt;&lt;em&gt;shoshin&lt;/em&gt;&lt;/a&gt;, or &quot;beginner&apos;s mind.&quot; As Shunryu Suzuki wrote in his classic book: &quot;In the beginner&apos;s mind there are many possibilities, but in the expert&apos;s mind there are few&quot;.&lt;/p&gt;
&lt;p&gt;This isn&apos;t just philosophical fluff. Research shows that &lt;a href=&quot;https://psyche.co/guides/how-to-cultivate-shoshin-or-a-beginners-mind&quot;&gt;intellectual humility - having a beginner&apos;s approach - is more of a state than a trait&lt;/a&gt;. It varies within us from situation to situation. Which means we need to actively cultivate it, constantly.&lt;/p&gt;
&lt;p&gt;The danger is real. The more expert we become, the more we fall victim to what psychologists call the &lt;strong&gt;Einstellung effect&lt;/strong&gt; - where a person becomes so accustomed to a certain way of doing things that they do not consider or acknowledge new ideas or approaches.&lt;/p&gt;
&lt;h2&gt;My Brazilian Jiu-Jitsu Reality Check&lt;/h2&gt;
&lt;p&gt;Last year, I decided to try Brazilian Jiu-Jitsu. I&apos;d done various martial arts for 15+ years, but groundwork was completely new to me. My experience with Judo and Japanese Jiu-Jitsu was mostly standing techniques and throws.&lt;/p&gt;
&lt;p&gt;Walking into that BJJ gym was humbling. I knew &lt;em&gt;nothing&lt;/em&gt;. Everyone - and I mean everyone - could destroy me with ease.&lt;/p&gt;
&lt;p&gt;But you know what? It was exhilarating.&lt;/p&gt;
&lt;p&gt;I became that annoying white belt asking constant questions: &quot;What happens if I do this? What if they grab here? How do I defend against that?&quot; I was the human embodiment of the beginner&apos;s mind, and I learned &lt;em&gt;fast&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Unfortunately, I had to stop. Turns out when you&apos;re my age, getting injured means days in bed with extreme back pain, muscle relaxants, and physiotherapy sessions. When you&apos;re young, you bounce back. When you&apos;re older... not so much. But I don&apos;t regret a single moment of getting my ass kicked on those mats.&lt;/p&gt;
&lt;h2&gt;The Voice AI Adventure&lt;/h2&gt;
&lt;p&gt;Here&apos;s a more recent example. My company wanted to build voice AI agents to handle phone calls. I knew absolutely nothing about voice AI.&lt;/p&gt;
&lt;p&gt;So what did I do? I watched a bunch of videos, built some terrible prototypes, and then went to an AI engineering conference last month where I spent all my time in the voice track.&lt;/p&gt;
&lt;p&gt;I learned about WebSocket vs WebRTC, how Voice Activity Detection (VAD) works, the difference between semantic VAD and basic activity detection, and the intricacies of pipeline architectures versus speech-to-speech models.&lt;/p&gt;
&lt;p&gt;Was it embarrassing asking basic questions while surrounded by experts? Sure. But now we have a working prototype that we&apos;re showing to customers, and we&apos;re expecting massive uptake soon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The satisfaction of going from zero to deployed product in a month? Priceless.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Breaking Out of the Middle Status Trap&lt;/h2&gt;
&lt;p&gt;So how do we escape the middle status trap? How do we give ourselves permission to be beginners again?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.themuse.com/advice/how-to-ask-stupid-questions-without-sounding-stupid&quot;&gt;Research on workplace questions&lt;/a&gt; shows that 70% of employees face barriers to asking questions, with fear of ridicule being the number one obstacle. Yet &lt;a href=&quot;https://www.joinglyph.com/blog/guide-to-overcoming-fear-of-asking-questions-at-work&quot;&gt;92% of managers believe curious people bring new ideas&lt;/a&gt; and see curiosity as driving innovation and high performance.&lt;/p&gt;
&lt;p&gt;The disconnect is obvious - we&apos;re afraid of something that would actually make us more valuable.&lt;/p&gt;
&lt;p&gt;Here&apos;s what works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Frame your ignorance as curiosity&lt;/strong&gt; - &quot;I&apos;m fascinated by this but want to make sure I understand...&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use your research&lt;/strong&gt; - &quot;I&apos;ve been reading about X and I&apos;m curious how it applies here...&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Make it about the team&lt;/strong&gt; - &quot;Let me ask what might seem like a basic question to make sure we&apos;re all aligned...&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Own your beginner status&lt;/strong&gt; - &quot;I&apos;m new to this area, so bear with me...&quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As &lt;a href=&quot;https://www.inc.com/bill-green/theres-no-such-thing-as-a-stupid-question-heres-how-you-can-empower-your-employees-to-ask-for-help.html&quot;&gt;one HR expert noted&lt;/a&gt;, successful entrepreneurs ask &quot;stupid&quot; questions all the time. That&apos;s because no question is &quot;stupid&quot; if you don&apos;t know already the answer.&lt;/p&gt;
&lt;h2&gt;The Real Message&lt;/h2&gt;
&lt;p&gt;Look, we can dress this up with fancy terms like &quot;growth mindset&quot; or &quot;continuous learning,&quot; but here&apos;s the brutal truth: &lt;strong&gt;the places where you know almost nothing are where you have the highest learning rate.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you only do things you&apos;re good at, you&apos;ll improve - slowly. But when you do things you suck at? That&apos;s where the magic happens. That&apos;s where you compress years of growth into months.&lt;/p&gt;
&lt;p&gt;The world is changing too fast for expertise to be a permanent state. &lt;a href=&quot;https://jamesclear.com/shoshin&quot;&gt;As James Clear writes about shoshin&lt;/a&gt;, &quot;There is a danger that comes with expertise. We tend to block the information that disagrees with what we learned previously and yield to the information that confirms our current approach&quot;.&lt;/p&gt;
&lt;h2&gt;The Bottom Line&lt;/h2&gt;
&lt;p&gt;The moat of low status isn&apos;t just about learning new skills. It&apos;s about recognizing that in a world changing at breakneck speed, your ability to be comfortably uncomfortable is your most valuable asset.&lt;/p&gt;
&lt;p&gt;And if you&apos;re stuck in that middle status zone? Remember that both the very successful and the seemingly unsuccessful share one trait - they&apos;re not afraid to look stupid. Maybe it&apos;s time to pick a side.&lt;/p&gt;
&lt;p&gt;Next time you&apos;re hesitating to try something because you might look stupid, remember: &lt;strong&gt;looking dumb is temporary, but ignorance is expensive.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;So go ahead. Be the first person on the dance floor. Ask the obvious question in the meeting. Try that thing you&apos;ve been putting off because you know you&apos;ll suck at it.&lt;/p&gt;
&lt;p&gt;The world won&apos;t turn to ash. And you might just surprise yourself.&lt;/p&gt;
</content:encoded></item><item><title>From MS-DOS to LLMs: Why This Is Computing&apos;s Next Great Transition</title><link>https://www.augusteo.com/blog/ms-dos-to-llms-computing-next-transition/</link><guid isPermaLink="true">https://www.augusteo.com/blog/ms-dos-to-llms-computing-next-transition/</guid><description>Andrej Karpathy&apos;s Y Combinator talk reveals how we&apos;re experiencing computing&apos;s most fundamental transformation in 70 years, as LLMs evolve from command-line interfaces to become the new operating systems that will reshape how we build and interact with software.</description><pubDate>Wed, 18 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Watch the full talk on YouTube: &lt;a href=&quot;https://www.youtube.com/watch?v=LCEmiRjPEtQ&quot;&gt;Andrej Karpathy: Software Is Changing (Again)&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I recently watched Andrej Karpathy&apos;s talk at Y Combinator on Youtube, and wow, it really crystallized a lot of thoughts I&apos;ve been having about where software development is heading. As someone who learned to code on MS-DOS more than 20 years ago (memorizing all those commands because there was no GUI!), Karpathy&apos;s comparison of today&apos;s LLMs to 1960s-era operating systems really hit home.&lt;/p&gt;
&lt;h2&gt;We&apos;re Living Through a Fundamental Shift&lt;/h2&gt;
&lt;p&gt;Karpathy opened with a bold claim: software hasn&apos;t changed this fundamentally in &lt;strong&gt;70 years&lt;/strong&gt;. And then suddenly, it&apos;s changed twice in just a few years.&lt;/p&gt;
&lt;p&gt;He breaks it down into three evolutionary stages:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/ms-dos-to-llms-computing-next-transition/ms-dos-to-llms-computing-next-transition-Screenshot_2025-06-18_at_8.27.29_PM.png&quot; alt=&quot;ms-dos-to-llms-computing-next-transition-Screenshot_2025-06-18_at_8.27.29_PM.png&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Software 1.0: The Code We Write&lt;/h3&gt;
&lt;p&gt;This is traditional programming - the C++, Python, JavaScript that humans write directly. It&apos;s explicit instructions telling computers exactly what to do.&lt;/p&gt;
&lt;h3&gt;Software 2.0: Neural Network Weights&lt;/h3&gt;
&lt;p&gt;A few years ago, Karpathy observed that neural networks represented a new kind of software. Instead of writing code, we&apos;re tuning datasets and running optimizers to create neural network parameters. He pointed to Hugging Face as the &quot;GitHub of Software 2.0&quot; - a place where these models live and evolve.&lt;/p&gt;
&lt;h3&gt;Software 3.0: Programming in English&lt;/h3&gt;
&lt;p&gt;Here&apos;s where things get wild. With large language models, we&apos;re now &lt;strong&gt;programming computers in natural language&lt;/strong&gt;. Your prompts are literally programs that configure these AI systems. As Karpathy tweeted (and it&apos;s now his pinned tweet): &quot;The hottest new programming language is English.&quot;&lt;/p&gt;
&lt;h2&gt;The Tesla Autopilot Story: A Preview of What&apos;s Coming&lt;/h2&gt;
&lt;p&gt;Karpathy shared a fascinating story from his time at Tesla. When he worked on Autopilot, he watched as neural networks literally &lt;strong&gt;ate through the software stack&lt;/strong&gt;. The C++ code was progressively deleted as more functionality moved from traditional code to neural nets.&lt;/p&gt;
&lt;p&gt;I think we&apos;re seeing the exact same pattern now. Traditional software is being consumed by AI, layer by layer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/ms-dos-to-llms-computing-next-transition/ms-dos-to-llms-computing-next-transition-Screenshot_2025-06-19_at_10.29.15_PM.png&quot; alt=&quot;ms-dos-to-llms-computing-next-transition-Screenshot_2025-06-19_at_10.29.15_PM.png&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;LLMs as the New Operating System&lt;/h2&gt;
&lt;p&gt;Here&apos;s where Karpathy&apos;s analysis gets really interesting - and where I strongly agree with him. LLMs aren&apos;t just utilities like electricity (though they share some characteristics). They&apos;re becoming more like &lt;strong&gt;operating systems&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Think about it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Like utilities&lt;/strong&gt;: We access them through metered APIs, pay per token, demand low latency and high uptime&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Like chip fabs&lt;/strong&gt;: Massive capital expenditure, deep tech trees, centralized expertise&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Like operating systems&lt;/strong&gt;: Complex software ecosystems with multiple providers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The comparison to operating systems is particularly apt. We have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A few closed-source providers (OpenAI, Anthropic, Google) = Windows/macOS&lt;/li&gt;
&lt;li&gt;Open-source alternatives (Llama ecosystem) = Linux&lt;/li&gt;
&lt;li&gt;Apps that can run on multiple &quot;OS&quot; options (Cursor works with GPT, Claude, or Gemini)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/ms-dos-to-llms-computing-next-transition/ms-dos-to-llms-computing-next-transition-Screenshot_2025-06-18_at_8.28.34_PM.png&quot; alt=&quot;ms-dos-to-llms-computing-next-transition-Screenshot_2025-06-18_at_8.28.34_PM.png&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;The Commodity Future&lt;/h3&gt;
&lt;p&gt;What really resonates with me is how LLMs are becoming commoditized. Just like we don&apos;t care where our electricity comes from (as long as it works), we&apos;re rapidly approaching a point where the specific LLM won&apos;t matter much. They&apos;ll all be highly capable and cheap. The winners in this era won&apos;t be the foundation model providers - it&apos;ll be the &lt;strong&gt;application layer&lt;/strong&gt;. Who will build the Microsoft Office or Adobe Photoshop of the LLM era?&lt;/p&gt;
&lt;h2&gt;The Psychology of LLMs: Autistic Savants with Superpowers&lt;/h2&gt;
&lt;p&gt;Karpathy&apos;s characterization of LLMs as &quot;people spirits&quot; with &quot;jagged intelligence&quot; is spot-on. They&apos;re like autistic savants who can memorize entire phone books but might insist that 9.11 &amp;gt; 9.9.&lt;/p&gt;
&lt;h3&gt;What They&apos;re Great At:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Encyclopedic knowledge and memory&lt;/li&gt;
&lt;li&gt;Processing massive amounts of information&lt;/li&gt;
&lt;li&gt;Pattern recognition across domains&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Their Cognitive Deficits:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Hallucinations and confabulations&lt;/li&gt;
&lt;li&gt;Jagged intelligence (superhuman in some areas, sub-human in others)&lt;/li&gt;
&lt;li&gt;Anterograde amnesia (can&apos;t form new long-term memories)&lt;/li&gt;
&lt;li&gt;Susceptibility to prompt injection and manipulation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, I think this talk is incomplete without mentioning the recent breakthroughs in &lt;strong&gt;thinking models&lt;/strong&gt; like OpenAI&apos;s o1 and o3. Many of the &quot;jagged intelligence&quot; problems Karpathy mentions are actually being solved through chain-of-thought reasoning. When we give LLMs more compute time to &quot;think,&quot; they can work through problems step-by-step, catching their own errors. It&apos;s slower, yes, but the accuracy improvements are dramatic.&lt;/p&gt;
&lt;h2&gt;Building in the Age of Partial Autonomy&lt;/h2&gt;
&lt;p&gt;The most practical part of Karpathy&apos;s talk focused on how we should actually build with these fallible but powerful systems. His key insight: we need &lt;strong&gt;partial autonomy apps&lt;/strong&gt; with:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/ms-dos-to-llms-computing-next-transition/ms-dos-to-llms-computing-next-transition-Screenshot_2025-06-18_at_8.29.33_PM.png&quot; alt=&quot;ms-dos-to-llms-computing-next-transition-Screenshot_2025-06-18_at_8.29.33_PM.png&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;1. Context Management&lt;/h3&gt;
&lt;p&gt;Apps shouldn&apos;t make users manually copy-paste between ChatGPT and their work. Look at Cursor - it automatically manages your codebase context.&lt;/p&gt;
&lt;h3&gt;2. Multiple Model Orchestration&lt;/h3&gt;
&lt;p&gt;Real apps need embedding models, chat models, diff models, etc. The complexity should be hidden from users.&lt;/p&gt;
&lt;h3&gt;3. Application-Specific GUIs&lt;/h3&gt;
&lt;p&gt;Text interfaces are terrible for humans! We need visual diffs, one-click accepts/rejects, and interfaces that utilize our &quot;computer vision GPU&quot; (our eyes and visual cortex).&lt;/p&gt;
&lt;h3&gt;4. The Autonomy Slider&lt;/h3&gt;
&lt;p&gt;This is crucial. Sometimes you want:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Low autonomy&lt;/strong&gt;: Tab completion in Cursor&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Medium autonomy&lt;/strong&gt;: Command+K to modify a code block&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;High autonomy&lt;/strong&gt;: Command+I to refactor your entire codebase&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key is keeping AI &quot;on a leash&quot; - getting work done without overwhelming humans with 10,000-line diffs we can&apos;t possibly review.&lt;/p&gt;
&lt;h2&gt;Vibe Coding: Everyone&apos;s a Programmer Now&lt;/h2&gt;
&lt;p&gt;Then there&apos;s &quot;vibe coding&quot; - Karpathy&apos;s term that unexpectedly went viral and now has its own Wikipedia page!&lt;/p&gt;
&lt;p&gt;I love his example of kids using AI to build apps. It&apos;s such a wholesome vision of the future. This isn&apos;t about replacing programmers - it&apos;s about making programming accessible to everyone. It&apos;s a gateway drug to software development.&lt;/p&gt;
&lt;h3&gt;My Take on Vibe Coding&lt;/h3&gt;
&lt;p&gt;Having tried it myself (and watched the ecosystem explode in 2025), vibe coding is real but has limits. The actual coding part has become trivial - tools like Cursor, v0, Claude Artifacts, and Windsurf can generate working prototypes in minutes. But here&apos;s the catch: &lt;strong&gt;everything else is still hard&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Karpathy&apos;s Menu Gen app perfectly illustrates this. He built the core functionality in hours, but then spent a &lt;strong&gt;week&lt;/strong&gt; on the &quot;real&quot; stuff:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Payments&lt;/li&gt;
&lt;li&gt;Domain setup&lt;/li&gt;
&lt;li&gt;Deployment&lt;/li&gt;
&lt;li&gt;All the DevOps clicking around&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is still the biggest barrier. We need to make the entire stack accessible to LLMs, not just the code generation part.&lt;/p&gt;
&lt;h2&gt;Building for Agents: The Next Frontier&lt;/h2&gt;
&lt;p&gt;The final piece of Karpathy&apos;s vision is perhaps the most forward-looking: &lt;strong&gt;we need to build for agents&lt;/strong&gt;, not just humans.&lt;/p&gt;
&lt;p&gt;This means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;llms.txt files&lt;/strong&gt; (like robots.txt) to help LLMs understand your site&lt;/li&gt;
&lt;li&gt;Documentation in &lt;strong&gt;markdown&lt;/strong&gt; instead of human-oriented formats&lt;/li&gt;
&lt;li&gt;Replacing &quot;click here&quot; instructions with &lt;strong&gt;curl commands&lt;/strong&gt; that agents can execute&lt;/li&gt;
&lt;li&gt;Tools like Gitingest that transform human interfaces into LLM-digestible formats&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We&apos;re meeting LLMs halfway, making our digital infrastructure speak their language.&lt;/p&gt;
&lt;h2&gt;Looking Ahead: The Decade of Agents&lt;/h2&gt;
&lt;p&gt;Karpathy&apos;s self-driving car story serves as a crucial reality check. He had a perfect Waymo demo in 2013 and thought autonomous driving was imminent. Twelve years later, we&apos;re still working on it.&lt;/p&gt;
&lt;p&gt;His prediction? This is the &lt;strong&gt;decade of agents&lt;/strong&gt; - not the year of agents. We need to be patient, keep humans in the loop, and gradually slide that autonomy slider from left to right.&lt;/p&gt;
&lt;h2&gt;The Bottom Line&lt;/h2&gt;
&lt;p&gt;What an incredible time to enter the tech industry! We&apos;re not just iterating on existing paradigms - we&apos;re completely rewriting how software works. Three different programming paradigms (traditional code, neural networks, natural language) are competing and complementing each other.&lt;/p&gt;
&lt;p&gt;The winners won&apos;t necessarily be those building the best LLMs. They&apos;ll be those who build the best &lt;strong&gt;experiences&lt;/strong&gt; on top of these new operating systems. Just like the biggest tech companies today aren&apos;t OS vendors but application builders.&lt;/p&gt;
&lt;p&gt;As someone who started with MS-DOS and watched GUIs revolutionize computing, I can&apos;t help but feel we&apos;re at a similar inflection point. We&apos;re in the MS-DOS era of AI - powerful but clunky, text-based, requiring memorization of commands. The GUI moment for AI is coming, and it&apos;s going to change everything.&lt;/p&gt;
&lt;p&gt;The future isn&apos;t about AI replacing programmers. It&apos;s about AI augmenting human creativity, making programming accessible to kids who just want to build cool stuff, and gradually automating the tedious parts while we focus on what matters: solving real problems for real people.&lt;/p&gt;
&lt;p&gt;Time to build! 🚀&lt;/p&gt;
</content:encoded></item><item><title>Book Review - &quot;An Absolutely Remarkable Thing&quot; by Hank Green</title><link>https://www.augusteo.com/blog/an-absolutely-remarkable-thing-hank-green-book-review/</link><guid isPermaLink="true">https://www.augusteo.com/blog/an-absolutely-remarkable-thing-hank-green-book-review/</guid><description>Captivating sci-fi debut from Hank Green that masterfully explores fame, social media addiction, and humanity&apos;s response to the unknown through the story of April May and mysterious alien statues</description><pubDate>Tue, 04 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Having been a fan of both John and Hank Green for years, I was excited to dive into Hank&apos;s debut novel. As a longtime follower of their work, I couldn&apos;t pass up the chance to see how Hank&apos;s storytelling would translate to fiction. Spoiler alert: he doesn&apos;t disappoint!&lt;/p&gt;
&lt;h2&gt;What I Loved About This Book&lt;/h2&gt;
&lt;p&gt;This book is &lt;strong&gt;captivating&lt;/strong&gt;. Like, seriously captivating. I finished it in 2-3 days because I simply couldn&apos;t put it down. While reading, I experienced a whole range of emotions—from anger at the characters to wonder about the dream sequences and puzzles—but &lt;em&gt;boredom was never one of them&lt;/em&gt;. I was constantly pulled forward, always wanting to know what would happen next.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;They tell you power corrupts. They never tell you how quickly.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This quote perfectly captures one of the book&apos;s central themes, and it stuck with me long after finishing the last page.&lt;/p&gt;
&lt;h2&gt;What&apos;s It About? (Major Spoilers Ahead!)&lt;/h2&gt;
&lt;p&gt;The story follows April May, who stumbles upon a giant robot statue in New York City one night. In my mind, I pictured it like a Gundam just standing there in the middle of Manhattan. April and her friend Andy decide to film it, and their video goes viral overnight.&lt;/p&gt;
&lt;p&gt;Here&apos;s where it gets interesting: identical statues (called &quot;Carls&quot;) have appeared simultaneously in major cities worldwide. These aren&apos;t just ordinary statues—they&apos;re made of unknown materials, impervious to damage, and they actually &lt;em&gt;float&lt;/em&gt;. Physics doesn&apos;t apply to them the way it does to everything else on Earth.&lt;/p&gt;
&lt;p&gt;As the world reacts to these mysterious appearances, people become divided on whether the Carls are a force for good or evil. Are they just observing us? Or are they planning to take over Earth?&lt;/p&gt;
&lt;h2&gt;The Dream Sequences: A Brilliant Plot Device&lt;/h2&gt;
&lt;p&gt;One of the most fascinating elements is how the Carls give everyone the same dream where they can lucid dream and work together to solve complex puzzles. These puzzles are deliberately designed to be cooperative—impossible to solve alone. They require diverse knowledge ranging from esoteric languages to obscure musical instruments and aviation engineering.&lt;/p&gt;
&lt;p&gt;April theorizes that the Carls are testing humanity&apos;s ability to collaborate, which adds an intriguing layer to the story.&lt;/p&gt;
&lt;h2&gt;Characters That Feel Real&lt;/h2&gt;
&lt;p&gt;While April is our protagonist, I particularly appreciated &lt;strong&gt;Maya&lt;/strong&gt;, her ex-girlfriend. Unlike other characters who often felt like sidekicks just helping April without question, Maya holds her own ground and tells it like it is. She&apos;s not afraid to challenge April&apos;s decisions and provides a necessary counterbalance to April&apos;s increasingly fame-driven choices.&lt;/p&gt;
&lt;p&gt;Other key relationships in the book include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Robin&lt;/strong&gt;, April&apos;s assistant&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Miranda&lt;/strong&gt;, a scientist who reaches out for help with the puzzles&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Andy&lt;/strong&gt;, her friend who helps create the initial viral video&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Fame, Social Media, and Our Modern World&lt;/h2&gt;
&lt;p&gt;The book delves deeply into how fame changes people, and it feels eerily relevant. We watch as April becomes addicted to her Twitter followers, talk show invitations, and public adoration. Her transformation from normal person to celebrity spokesperson for the Carls happens at breakneck speed, showing how &lt;em&gt;quickly&lt;/em&gt; power can indeed corrupt.&lt;/p&gt;
&lt;p&gt;There&apos;s also a powerful exploration of xenophobia and fear-mongering through characters like Peter Petrawicki, who writes a book called &quot;Invaded&quot; arguing that the Carls are dangerous. This aspect feels very reminiscent of real-world politics, where fear is peddled to gain power and popularity—a rather Trumpian tactic that the book critiques effectively.&lt;/p&gt;
&lt;h2&gt;The Climactic Ending&lt;/h2&gt;
&lt;p&gt;One of the most memorable moments comes near the end when April is trapped in a burning warehouse. The scene where she breaks through a window only to be crushed by a falling beam is heartbreaking—but then the twist comes when we learn she&apos;s somehow been transported into the Dream where she can speak directly with Carl. The revelation that Carl specifically chose her for first contact adds a whole new dimension to the story.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;While I wouldn&apos;t call it a perfect 5-star book that I&apos;ll repeatedly return to, &quot;An Absolutely Remarkable Thing&quot; is definitely a solid 4-star read that I thoroughly enjoyed. The cliffhanger ending with April&apos;s cryptic &quot;Knock Knock&quot; text message has me eager to pick up the sequel, &quot;A Beautifully Foolish Endeavor.&quot;&lt;/p&gt;
&lt;p&gt;If you enjoy science fiction with a contemporary twist, explorations of social media fame, and mysteries that unfold in unexpected ways, this book is absolutely worth your time.&lt;/p&gt;
</content:encoded></item><item><title>From Swift to Rails: My Foray Into Non-Trivial Ruby on Rails</title><link>https://www.augusteo.com/blog/swift-to-rails-learning-design-patterns/</link><guid isPermaLink="true">https://www.augusteo.com/blog/swift-to-rails-learning-design-patterns/</guid><description>My journey into Ruby on Rails, exploring the differences in design patterns, type safety challenges, and discovering how delegators and resources can create cleaner, more maintainable code.</description><pubDate>Mon, 03 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve been diving into Ruby on Rails after years of working with Swift for iOS and React. The mental shift has been quite interesting, and I wanted to share my observations while they&apos;re still fresh.&lt;/p&gt;
&lt;h2&gt;MVC vs. MVVM: Not What I Expected&lt;/h2&gt;
&lt;p&gt;Coming from the iOS world, I&apos;m thoroughly accustomed to the MVVM (Model-View-ViewModel) pattern. iOS originally used MVC with UIKit, but Rails&apos; implementation of MVC differs significantly from what I anticipated.&lt;/p&gt;
&lt;p&gt;In iOS with CoreData, &lt;strong&gt;model&lt;/strong&gt; files are typically small and lightweight, while the &lt;strong&gt;controller&lt;/strong&gt; ends up doing most of the heavy lifting. This imbalance is precisely why iOS shifted toward MVVM – controllers were becoming unwieldy and overly complex. The ViewModel serves &lt;em&gt;somewhat&lt;/em&gt; like a controller, but its role is more focused – it doesn&apos;t directly control the view; rather, it supplies the data for the view to respond to.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In Rails, I discovered the opposite problem.&lt;/strong&gt; Controllers are designed to be thin, while models tend to become bloated! Developers often add increasing amounts of business logic into model files, transforming them from simple data definitions into repositories of helper methods and modifiers.&lt;/p&gt;
&lt;p&gt;Here&apos;s what a &quot;fat&quot; Rails model typically looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Car &amp;lt; ApplicationRecord
  # Basic attributes: make, model, year

  # Validations
  validates :make, presence: true
  validates :year, numericality: { only_integer: true }

  # Business logic
  def legal_in_america?
    current_year = Date.today.year
    self.year &amp;gt;= (current_year - 10)
  end

  # More business logic
  def full_name
    &quot;#{self.year} #{self.make} #{self.model}&quot;
  end

  # Price calculation logic
  def depreciated_value(original_price)
    age = Date.today.year - self.year
    depreciation_factor = [0.1 * age, 0.9].min
    original_price * (1 - depreciation_factor)
  end

  # More methods...
  def needs_emission_test?
    self.year &amp;lt; Date.today.year - 2
  end

  # And more methods...
  def registration_fee
    base_fee = 100
    extra_fee = Date.today.year - self.year
    base_fee + (extra_fee * 5)
  end
end

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Swift MVVM, this logic would naturally reside in the ViewModel, not the model. Yet in Rails, all of it tends to accumulate in the model.&lt;/p&gt;
&lt;h2&gt;The Delegation Solution: Keeping Models Slim&lt;/h2&gt;
&lt;p&gt;As I explored further, I discovered additional components in the Rails ecosystem that address this problem quite elegantly. One particularly useful solution is using the &lt;strong&gt;SimpleDelegator&lt;/strong&gt; pattern.&lt;/p&gt;
&lt;p&gt;Ruby&apos;s standard library provides a &lt;code&gt;SimpleDelegator&lt;/code&gt; class that allows you to wrap an object and delegate method calls to it, while also adding additional functionality. This is perfect for extracting logic from models without losing access to their attributes and methods.&lt;/p&gt;
&lt;p&gt;Here&apos;s how it works:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# The slimmed-down model
class Car &amp;lt; ApplicationRecord
  # Just basic attributes and validations
  validates :make, presence: true
  validates :year, numericality: { only_integer: true }
end

# A decorator that adds legal-related functionality
class CarLegalDecorator &amp;lt; SimpleDelegator
  def legal_in_america?
    current_year = Date.today.year
    year &amp;gt;= (current_year - 10)
  end

  def needs_emission_test?
    year &amp;lt; Date.today.year - 2
  end
end

# A decorator for financial concerns
class CarFinanceDecorator &amp;lt; SimpleDelegator
  def depreciated_value(original_price)
    age = Date.today.year - year
    depreciation_factor = [0.1 * age, 0.9].min
    original_price * (1 - depreciation_factor)
  end

  def registration_fee
    base_fee = 100
    extra_fee = Date.today.year - year
    base_fee + (extra_fee * 5)
  end
end

# Usage in a controller
def show
  @car = Car.find(params[:id])
  @legal_car = CarLegalDecorator.new(@car)

  if @legal_car.legal_in_america?
    # Do something
  end
end

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The beauty of &lt;code&gt;SimpleDelegator&lt;/code&gt; is that it automatically forwards method calls to the wrapped object. Notice how in the &lt;code&gt;legal_in_america?&lt;/code&gt; method, I can directly call &lt;code&gt;year&lt;/code&gt; instead of &lt;code&gt;car.year&lt;/code&gt; because the method call is delegated to the Car object.&lt;/p&gt;
&lt;p&gt;This approach brings several advantages. Controllers can use only the specific decorator they need, without having to know about all the other business logic. For instance, only the legal controller needs to use the &lt;code&gt;CarLegalDecorator&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Presentation Layer: JSON Resources&lt;/h2&gt;
&lt;p&gt;Another powerful component I discovered is the &lt;strong&gt;JSON Resources&lt;/strong&gt; concept, which functions as a presenter layer. In Rails, libraries like Alba provide a clean way to define how to present a model as JSON.&lt;/p&gt;
&lt;p&gt;Here&apos;s how a resource would look using the Alba gem:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# frozen_string_literal: true
class CarResource
  include Alba::Resource

  attributes :id, :make, :model, :year

  # Derived attributes
  attribute :name
  attribute :is_legal
  attribute :registration_status
  attribute :owner_info
  attribute :maintenance_history

  # Access decorated car for legal checks
  def decorated_car(car)
    @decorated_car ||= CarLegalDecorator.new(car)
  end

  # Access finance decorator for financial calculations
  def finance_decorator(car)
    @finance_decorator ||= CarFinanceDecorator.new(car)
  end

  def name(car)
    &quot;#{car.year} #{car.make} #{car.model}&quot;
  end

  def is_legal(car)
    decorated_car(car).legal_in_america?
  end

  def registration_status(car)
    car.year &amp;lt; Date.today.year - 5 ? &quot;Needs renewal&quot; : &quot;Valid&quot;
  end

  def owner_info(car)
    car.owner&amp;amp;.name || &quot;No owner information&quot;
  end

  def maintenance_history(car)
    car.maintenance_records&amp;amp;.map do |record|
      {
        date: record.service_date,
        description: record.description,
        cost: finance_decorator(car).adjust_for_inflation(record.cost)
      }
    end || []
  end
end

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then in the controller, instead of building complex JSON structures, you simply use the resource:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class CarsController &amp;lt; ApplicationController
  def index
    @cars = Car.includes(:owner, :maintenance_records).all

    respond_to do |format|
      format.html # Render the index.html.erb template
      format.json { render json: @cars.map { |car| CarResource.new(car).serializable_hash } }
    end
  end
end

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The real power becomes evident when using this in an ERB template with data grids like AG Grid:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;%# In your index.html.erb template %&amp;gt;
&amp;lt;div id=&quot;cars-grid&quot; class=&quot;ag-theme-alpine&quot; style=&quot;height: 500px; width: 100%;&quot;&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;script&amp;gt;
  document.addEventListener(&apos;DOMContentLoaded&apos;, function() {
    new agGrid.Grid(document.querySelector(&apos;#cars-grid&apos;), {
      columnDefs: [
        { headerName: &apos;Car&apos;, field: &apos;name&apos; },
        { headerName: &apos;Legal Status&apos;, field: &apos;is_legal&apos;,
          cellRenderer: params =&amp;gt; params.value ? &apos;Legal&apos; : &apos;Illegal&apos; },
        { headerName: &apos;Registration&apos;, field: &apos;registration_status&apos; },
        { headerName: &apos;Owner&apos;, field: &apos;owner_info&apos; },
        // More columns...
      ],
      // The key part - using our resource to format the data
      rowData: &amp;lt;%= raw @cars.map { |car| CarResource.new(car).serializable_hash }.to_json %&amp;gt;,
      defaultColDef: {
        sortable: true,
        filter: true
      }
    });
  });
&amp;lt;/script&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach dramatically simplifies both ERB/HTML views and controllers. The resource becomes the single source of truth for how your data is presented, while keeping presentation logic out of both models and controllers.&lt;/p&gt;
&lt;p&gt;Here&apos;s an updated diagram illustrating how these components work together:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/swift-to-rails-learning-design-patterns/swift-to-rails-learning-design-patterns-mermaid-diagram-2025-03-02-171124.svg&quot; alt=&quot;swift-to-rails-learning-design-patterns-mermaid-diagram-2025-03-02-171124.svg&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The BFF Pattern: Views as API Clients&lt;/h2&gt;
&lt;p&gt;Taking the Resource concept a step further, I&apos;ve discovered another powerful pattern in Rails that blurs the traditional MVC boundaries: &lt;strong&gt;treating views as API clients&lt;/strong&gt;. In this Backend-For-Frontend (BFF) pattern, the view makes AJAX calls back to the same controller but with a different format (JSON).&lt;/p&gt;
&lt;p&gt;Here&apos;s how I implemented this with our car example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# app/controllers/cars_controller.rb
# frozen_string_literal: true
class CarsController &amp;lt; ApplicationController
  before_action { @page_title = &quot;Car Inventory&quot; }
  before_action :set_car, only: %i[show update]

  def index
    respond_to do |format|
      format.html # Renders the index.html.erb template (lightweight)
      format.json do
        # Heavy database query only happens for JSON requests
        cars = Car
          .where(manufactured_year: 5.years.ago.year..)
          .order(created_at: :desc)
          .includes(:owner, :maintenance_records)

        # Use the resource for JSON serialization
        render json: CarResource.new(cars).serialize
      end
    end
  end

  def show
    respond_to do |format|
      format.html
      format.json { render json: CarResource.new(@car).serialize }
    end
  end

  private

  def set_car
    @car = Car.find(params[:id])
  end
end

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The corresponding view then makes a fetch call to retrieve this JSON data:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- app/views/cars/index.html.erb --&amp;gt;
&amp;lt;div class=&quot;mb-10 flex relative mx-auto items-center space-x-4&quot;&amp;gt;
  &amp;lt;!-- Filter controls for the grid --&amp;gt;
  &amp;lt;div class=&quot;w-1/2&quot;&amp;gt;
    &amp;lt;div class=&quot;inline-flex flex-nowrap whitespace-nowrap border border-blue-500 bg-white rounded-lg overflow-hidden&quot;&amp;gt;
      &amp;lt;button type=&quot;button&quot; class=&quot;px-6 py-3 text-blue-500 font-medium focus:outline-none hover:bg-blue-100&quot;&amp;gt;
        All
      &amp;lt;/button&amp;gt;
      &amp;lt;button type=&quot;button&quot; class=&quot;px-6 py-3 text-blue-500 font-medium focus:outline-none hover:bg-blue-100&quot;&amp;gt;
        Legal
      &amp;lt;/button&amp;gt;
      &amp;lt;button type=&quot;button&quot; class=&quot;px-6 py-3 text-blue-500 font-medium focus:outline-none hover:bg-blue-100&quot;&amp;gt;
        Needs Renewal
      &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div id=&quot;carsGrid&quot; class=&quot;ag-theme-alpine&quot; style=&quot;width: 100%; height: 600px;&quot;&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;script type=&quot;module&quot;&amp;gt;
  import { createGrid } from &quot;ag-grid-community&quot;;

  document.addEventListener(&quot;turbo:load&quot;, () =&amp;gt; {
    initializeGrid();
  });

  function initializeGrid() {
    const gridContainer = document.getElementById(&quot;carsGrid&quot;);
    if (!gridContainer) return;

    const columnDefs = [
      {
        headerName: &apos;CAR&apos;,
        field: &quot;name&quot;,
        minWidth: 180,
        cellRenderer: params =&amp;gt; {
          const id = params.data?.id;
          const name = params.data?.name;
          return `&amp;lt;a href=&quot;/cars/${id}&quot; data-turbo=&quot;false&quot;&amp;gt;${name}&amp;lt;/a&amp;gt;`;
        }
      },
      {
        headerName: &apos;LEGAL STATUS&apos;,
        field: &quot;is_legal&quot;,
        minWidth: 120,
        cellRenderer: params =&amp;gt; params.value ? &quot;Legal&quot; : &quot;Illegal&quot;
      },
      {
        headerName: &apos;REGISTRATION&apos;,
        field: &quot;registration_status&quot;,
        minWidth: 150
      },
      {
        headerName: &apos;OWNER&apos;,
        field: &quot;owner_info&quot;,
        minWidth: 200
      }
    ];

    const gridOptions = {
      columnDefs: columnDefs,
      rowData: [],
      defaultColDef: {
        flex: 1,
        filter: true,
        sortable: true
      },
      onGridReady: function(params) {
        fetchDataAndSetRows(params.api);
      }
    };

    createGrid(gridContainer, gridOptions);
  }

  function fetchDataAndSetRows(gridApi) {
    fetch(&quot;/cars.json&quot;) // Calls the same controller with .json format
      .then(response =&amp;gt; response.json())
      .then(data =&amp;gt; {
        gridApi.setGridOption(&apos;rowData&apos;, data);
      })
      .catch(error =&amp;gt; console.error(&quot;Error loading grid data:&quot;, error));
  }
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What makes this pattern interesting is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Separation of concerns&lt;/strong&gt;: The initial HTML render is lightweight and quick, while the data-heavy operation happens asynchronously.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource reuse&lt;/strong&gt;: The same Resource used for API endpoints can be reused for internal view components.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Progressive enhancement&lt;/strong&gt;: The page is functional even before the JS-driven grid loads.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance optimization&lt;/strong&gt;: Including complex data in the initial HTML render can slow down time-to-first-render, but this pattern avoids that.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This pattern combines the best of both worlds: Rails&apos; server-side rendering for the initial page load and API-driven client-side rendering for data-heavy components. It feels very similar to how modern frontend frameworks operate, but without requiring a separate API backend.&lt;/p&gt;
&lt;h2&gt;The Missing Types: Swift vs. Ruby&lt;/h2&gt;
&lt;p&gt;The second major difference that struck me is how much I&apos;ve come to rely on Swift&apos;s type system! Ruby&apos;s dynamic typing means there&apos;s no clear distinction between methods, functions, and variables as in Swift. Everything is simply defined with &lt;code&gt;def&lt;/code&gt;, and you call methods on objects without any guarantee they&apos;ll respond appropriately.&lt;/p&gt;
&lt;p&gt;The challenge here is that you can&apos;t depend on the compiler to verify correctness before deployment. Instead, you must write more comprehensive unit tests and adopt a defensive coding style.&lt;/p&gt;
&lt;p&gt;In Swift, I could write something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Car {
    let make: String
    let model: String
    let year: Int

    func isLegalInAmerica() -&amp;gt; Bool {
        let currentYear = Calendar.current.component(.year, from: Date())
        return year &amp;gt;= (currentYear - 10)
    }
}

// Function with optional
func processRegistration(for car: Car?) {
    // Using guard let for early return
    guard let car = car else {
        print(&quot;No car provided&quot;)
        return
    }

    // Now we know car is not nil
    if car.isLegalInAmerica() {
        print(&quot;Car is legal: \(car.make) \(car.model)&quot;)
    } else {
        print(&quot;Car is too old to be registered&quot;)
    }
}

// Or with if-let
func displayCarInfo(car: Car?) {
    if let car = car {
        // Safe to use car here
        print(&quot;\(car.year) \(car.make) \(car.model)&quot;)
    } else {
        print(&quot;No car information available&quot;)
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ruby offers no such safety net:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def process_registration(car)
  # No compile-time guarantee that car exists or has these methods
  if car &amp;amp;&amp;amp; car.legal_in_america?
    puts &quot;Car is legal: #{car.make} #{car.model}&quot;
  else
    puts &quot;Car is too old or invalid&quot;
  end
end

# Using the safe navigation operator (&amp;amp;.)
def display_car_info(car)
  puts &quot;#{car&amp;amp;.year} #{car&amp;amp;.make} #{car&amp;amp;.model}&quot;
end

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Ruby, virtually anything can be nil. While you can use the safe accessor (&lt;code&gt;&amp;amp;.&lt;/code&gt;) similar to Swift&apos;s optional chaining, there&apos;s no equivalent to the elegant &lt;code&gt;guard let&lt;/code&gt; or &lt;code&gt;if let&lt;/code&gt; constructs. You&apos;re forced to manually check for nil values, resulting in more defensive coding practices.&lt;/p&gt;
&lt;h2&gt;Solving the Type Safety Issue&lt;/h2&gt;
&lt;p&gt;To address these type safety concerns, Ruby developers rely on tools like:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;RuboCop&lt;/strong&gt; - A linter that catches common coding issues&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sorbet&lt;/strong&gt; - A static type checker designed for Ruby&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;My current project employs RuboCop, but we&apos;re planning to implement Sorbet soon since we&apos;ve encountered numerous &quot;method not found&quot; errors when objects unexpectedly return nil.&lt;/p&gt;
&lt;h2&gt;Conclusion: Different But Fascinating&lt;/h2&gt;
&lt;p&gt;Ruby on Rails is compelling because it provides established conventions that enable rapid development and productivity, even when working with unfamiliar codebases. If you know Rails well, you can join a new company and become productive quickly because they&apos;re likely following these same conventions.&lt;/p&gt;
&lt;p&gt;The main drawback is the absence of static typing. After working extensively with Swift, TypeScript, and Go, I&apos;ve grown to appreciate the safety and performance benefits of typed languages. While Ruby may be less performant, for most applications, raw speed isn&apos;t the primary concern.&lt;/p&gt;
&lt;p&gt;What concerns me more is the potential for bugs arising from the lack of type safety. Fortunately, there appear to be effective mitigations through tools like Sorbet and architectural patterns using delegators and resources.&lt;/p&gt;
&lt;p&gt;I&apos;m excited to explore these solutions further in the coming weeks. Stay tuned for more updates on my journey from Swift to Rails!&lt;/p&gt;
</content:encoded></item><item><title>Spicing Up Your Team Stand-ups with &quot;Question of the Day&quot;</title><link>https://www.augusteo.com/blog/team-stand-up-question-of-the-day-fun-idea/</link><guid isPermaLink="true">https://www.augusteo.com/blog/team-stand-up-question-of-the-day-fun-idea/</guid><description>Adding a fun &quot;Question of the Day&quot; to your daily stand-ups helps your team actually get to know each other and makes meetings way less boring!</description><pubDate>Sun, 02 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every day, countless teams worldwide participate in daily stand-ups or scrums. It&apos;s that short meeting where everyone shares what they did yesterday, what they plan to do today, and mentions any blockers they&apos;re facing.&lt;/p&gt;
&lt;p&gt;But let&apos;s be honest - &lt;strong&gt;these meetings can quickly become routine&lt;/strong&gt;. People show up, give their updates mechanically, and miss out on a perfect opportunity to actually connect with their teammates.&lt;/p&gt;
&lt;h2&gt;Enter &quot;Question of the Day&quot;&lt;/h2&gt;
&lt;p&gt;A few weeks ago, Sean from my team introduced something called &lt;strong&gt;&quot;Question of the Day&quot;&lt;/strong&gt;. The concept is simple: whoever moderates the stand-up asks one random, fun question that helps us learn more about each other.&lt;/p&gt;
&lt;p&gt;Some examples of questions we&apos;ve used:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What&apos;s your favorite caffeinated drink?&lt;/li&gt;
&lt;li&gt;What&apos;s your favorite ice cream flavor?&lt;/li&gt;
&lt;li&gt;What&apos;s your favorite candy?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But it doesn&apos;t always have to be about favorites! We also ask things like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Share a fun fact about yourself&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;What would you do with an elephant if it were gifted to you and you couldn&apos;t sell it or give it away?&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The answers can get pretty wild, and that&apos;s part of the fun!&lt;/p&gt;
&lt;h2&gt;How We Organize It&lt;/h2&gt;
&lt;p&gt;We rotate moderators weekly, which gives each person time to prepare a week&apos;s worth of questions. They&apos;re free to come up with questions on their own or generate them using an LLM if they want.&lt;/p&gt;
&lt;p&gt;Every Friday, we use a spinning wheel to pick the next week&apos;s moderator. Once someone has been a moderator, they&apos;re removed from the wheel for that rotation. When everyone has had a turn, we reset the wheel and start over.&lt;/p&gt;
&lt;h2&gt;The Impact&lt;/h2&gt;
&lt;p&gt;We&apos;ve only been doing this for several weeks, but I&apos;ve already noticed we&apos;re starting to know each other better outside of work context. &lt;strong&gt;It&apos;s been great for team morale!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &quot;Question of the Day&quot; brings a human element to our stand-ups, which is especially valuable in a remote setting where we don&apos;t get many chances to see each other in person. Since we have to set up explicit calls to talk, having this built into our existing meetings makes connecting more natural.&lt;/p&gt;
&lt;p&gt;I highly recommend giving this practice a try with your own team! Find your own style and questions that work for your group - it&apos;s a small change that can make a big difference in how your team connects.&lt;/p&gt;
</content:encoded></item><item><title>Why I Left Apple to join Boon</title><link>https://www.augusteo.com/blog/why-i-left-apple-to-join-boon/</link><guid isPermaLink="true">https://www.augusteo.com/blog/why-i-left-apple-to-join-boon/</guid><description>After seven years at Apple, I joined Boon, a logistics AI startup, seeking greater growth potential and leveraging a team with deep domain expertise that knows how to sell what I know how to build</description><pubDate>Fri, 28 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When I interview candidates at my new company Boon, I always give them time at the end to ask questions. Without fail, almost everyone asks me the same thing: &lt;strong&gt;Why did I leave Apple to join a small startup?&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;The Apple Years&lt;/h2&gt;
&lt;p&gt;At the beginning of these interviews, I typically introduce myself - sharing my experience in the industry, previous projects I&apos;ve built, and mentioning that I spent the last 7 years at Apple where I worked on various projects.&lt;/p&gt;
&lt;p&gt;Given how often I&apos;m asked why I left, I&apos;ve developed a mental script that I can recite almost automatically.&lt;/p&gt;
&lt;h2&gt;Why I Left Apple&lt;/h2&gt;
&lt;p&gt;The simple answer? &lt;strong&gt;Growth potential.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Apple has grown tremendously since I joined in 2017. When I started, their stock price was around $38 (post-split). Today, it&apos;s approximately $230 – that&apos;s about a 5x growth in just 7 years. While impressive, I don&apos;t have confidence that Apple can achieve another 5x growth in the next 7 years.&lt;/p&gt;
&lt;p&gt;Here&apos;s my thinking:&lt;/p&gt;
&lt;p&gt;Apple now builds &lt;em&gt;really good devices&lt;/em&gt; that people don&apos;t need to replace as frequently anymore. Think about the leap from iPhone 4 to iPhone 5 – it was such a dramatic improvement that everyone wanted to upgrade immediately. The same thing happened when Apple Silicon replaced Intel chips in MacBooks.&lt;/p&gt;
&lt;p&gt;But I&apos;m still using my MacBook M1, and it works perfectly fine. &lt;strong&gt;I don&apos;t feel any need to upgrade yet.&lt;/strong&gt; This is actually a testament to Apple&apos;s quality, but ironically, it&apos;s not great for continued stock growth.&lt;/p&gt;
&lt;p&gt;While Apple is focusing on growing its services business, I&apos;m not convinced that will be enough to 5x the company&apos;s value in the next 5-7 years.&lt;/p&gt;
&lt;h2&gt;The Big Tech Dilemma&lt;/h2&gt;
&lt;p&gt;This growth ceiling isn&apos;t unique to Apple. If I joined another Big Tech company, they&apos;d likely face the same challenge - they&apos;re already so large that dramatic growth becomes increasingly difficult.&lt;/p&gt;
&lt;h2&gt;Why Not Other Startups?&lt;/h2&gt;
&lt;p&gt;During my job search, I actually interviewed with five or six different startups across various industries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A startup doing voice AI in healthcare&lt;/li&gt;
&lt;li&gt;Another in home defense and security&lt;/li&gt;
&lt;li&gt;Several startups working on RAG (Retrieval-Augmented Generation) stacks for enterprise, helping companies search and write documents&lt;/li&gt;
&lt;li&gt;A few others in different spaces&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I deliberately chose &lt;em&gt;not&lt;/em&gt; to join these companies for specific reasons:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Security companies&lt;/strong&gt;: Security often feels like an afterthought for most consumers - we don&apos;t typically buy security products because most of the time we feel safe enough. More importantly, I didn&apos;t want to be responsible if software I built accidentally harmed someone innocent. That would be terrible, and it&apos;s a responsibility I preferred to avoid.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Healthcare companies&lt;/strong&gt;: Having spent a couple of years at Apple Health, I had firsthand experience with the challenges in this industry. FDA approval processes and selling to insurance companies create incredibly long sales cycles. It&apos;s a difficult space with many regulatory hurdles.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RAG stack companies&lt;/strong&gt;: &lt;em&gt;Everyone&lt;/em&gt; is building these right now. There are so many competitors in this space that I couldn&apos;t see how a single company could successfully compete against much larger players like OpenAI and Google Gemini, who are also building similar solutions.&lt;/p&gt;
&lt;h2&gt;Why I Chose Boon&lt;/h2&gt;
&lt;p&gt;For startups, the biggest challenge is finding product-market fit. You want to enter an industry with:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A &lt;em&gt;reasonable&lt;/em&gt; number of competitors (not zero, which would suggest no market)&lt;/li&gt;
&lt;li&gt;Not &lt;em&gt;too many&lt;/em&gt; competitors&lt;/li&gt;
&lt;li&gt;A product with some competitive moat and velocity&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;What made Boon stand out to me was the &lt;strong&gt;team&apos;s domain expertise&lt;/strong&gt;. The CEO and founder, Deepti, came from Samsara, a public company in the transportation and logistics industry. The team has been working in logistics for a while and really understands:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What to sell&lt;/li&gt;
&lt;li&gt;Who to sell it to&lt;/li&gt;
&lt;li&gt;How to sell it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Boon is building AI software to help logistics and transportation companies optimize their operations, and the team already has deep industry knowledge.&lt;/p&gt;
&lt;p&gt;As an engineer, I&apos;m confident in my ability to build things. What I can&apos;t necessarily do is sell. This makes for a perfect partnership: &lt;strong&gt;they know how to sell, and I know how to build&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;My conversations with Deepti have been great, and I&apos;m genuinely enjoying working with her. I believe Boon has the potential to become a major player in the industry within a few years. We just need to execute well.&lt;/p&gt;
&lt;p&gt;And that&apos;s why I made the switch from Apple to Boon.&lt;/p&gt;
</content:encoded></item><item><title>Book Review - &quot;Wind and Truth&quot; (Stormlight Archive Book 5)</title><link>https://www.augusteo.com/blog/wind-and-truth-review/</link><guid isPermaLink="true">https://www.augusteo.com/blog/wind-and-truth-review/</guid><description>&quot;Wind and Truth&quot; (Stormlight Archive Book 5) delivers interesting character moments but focuses too heavily on mental health and contract law instead of epic fantasy action, earning it 3.5/5 stars.</description><pubDate>Thu, 27 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I just finished listening to the audiobook of &lt;strong&gt;&quot;Wind and Truth&quot;&lt;/strong&gt; yesterday, and I wanted to write up my thoughts while they&apos;re still fresh in my mind. This is a &lt;em&gt;massive&lt;/em&gt; book—the audiobook is 65 hours long—and it took me almost two months to get through it since I don&apos;t listen to audiobooks that much anymore.&lt;/p&gt;
&lt;h2&gt;My Background with the Series&lt;/h2&gt;
&lt;p&gt;Before diving into my review, I should mention that I&apos;m a &lt;strong&gt;huge fan&lt;/strong&gt; of this series. I&apos;ve read all the previous books (one through four) twice, and I even watched a comprehensive three-hour-long &lt;a href=&quot;https://www.youtube.com/watch?v=uU9QZD6dyss&quot;&gt;recap video on YouTube&lt;/a&gt; before starting this fifth book. Suffice to say, I&apos;m a big fan of both the Stormlight Archive and Brandon Sanderson in general, so I had high expectations going in.&lt;/p&gt;
&lt;h2&gt;The Rating: ★★★½ (3.5/5)&lt;/h2&gt;
&lt;p&gt;I&apos;m giving this book &lt;strong&gt;3.5 out of 5 stars&lt;/strong&gt;. It&apos;s &lt;em&gt;almost&lt;/em&gt; great—almost a 4-star book—but it&apos;s riding mostly on the coattails of the previous books in the series. This book by itself has several problems that kept it from reaching the heights of books one and two. Instead, it feels closer to book four, where Sanderson started focusing more heavily on mental health challenges and diving deeper into those themes rather than the epic fantasy action that initially drew me to the series.&lt;/p&gt;
&lt;h2&gt;The Structure and Plot&lt;/h2&gt;
&lt;p&gt;The book is structured around a &quot;Contest of Champions&quot; that will take place in 10 days between the god Odium and humanity under Dalinar Kholin. The narrative follows a chronological structure, with each section covering one of these ten days. A lot happens during this short timeframe—that&apos;s the whole premise, after all. Something big is coming in ten days.&lt;/p&gt;
&lt;p&gt;However, the way the author chose to set up and advance the plot feels suboptimal. I did enjoy learning about humanity&apos;s arrival on Roshar and their interactions with the native Singers, who had a more advanced civilization. The introduction of the Unmade and their attempt to seize god-like power was interesting but a bit convoluted. After all, the Unmade were supposedly created by Odium&apos;s power—they shouldn&apos;t be able to rebel against him and make their own decisions.&lt;/p&gt;
&lt;h2&gt;Character Arcs&lt;/h2&gt;
&lt;h3&gt;Kaladin and Szeth&lt;/h3&gt;
&lt;p&gt;The storyline following Szeth and Kaladin as they travel to Shadesmar to figure out what&apos;s wrong with the Heralds was, honestly, quite painful to read. It could have been much faster-paced with more action sequences. Instead, there&apos;s a lot of Kaladin trying to provide therapy to Szeth and the Heralds. It just felt odd having this therapist-like character attempting to help demigod beings who have lived for thousands of years.&lt;/p&gt;
&lt;h3&gt;Adolin&lt;/h3&gt;
&lt;p&gt;The character development for Adolin was the best part of the book. His arc features plenty of action, fighting, and progress as he confronts good and evil. The surprise ending, where Maya breaks through to help Adolin, the Emperor, and the other survivors, was genuinely fun and exciting.&lt;/p&gt;
&lt;h3&gt;Shallan&lt;/h3&gt;
&lt;p&gt;Shallan&apos;s part was okay, but not great. It didn&apos;t leave much of an impression on me.&lt;/p&gt;
&lt;h3&gt;Dalinar&lt;/h3&gt;
&lt;p&gt;Dalinar&apos;s storyline was slightly more interesting, primarily because it revealed the past of how humanity came to Roshar and how they interacted with the Singers. I felt like this part had potential but wasn&apos;t fully realized.&lt;/p&gt;
&lt;h3&gt;Wit&lt;/h3&gt;
&lt;p&gt;I would have loved to read more about Wit, but he mostly serves as a character that ties the different books together. There are mentions of Scadrial (the Mistborn planet), suggesting there might be a battle there in the future—perhaps in Mistborn Era 3. This is the kind of cosmere connection I enjoy, but it wasn&apos;t explored deeply enough.&lt;/p&gt;
&lt;h2&gt;What Didn&apos;t Work&lt;/h2&gt;
&lt;p&gt;The book focuses too much on mental health issues, sexuality, and even contract law—the very things many of us read fantasy books to escape from, as we already deal with them enough in the real world. I was hoping for more scenes of flying knights battling villains (or villains who turn out to be good guys), but this book takes a different approach.&lt;/p&gt;
&lt;p&gt;The Ghostbloods felt pretty weak in this installment. Their presence didn&apos;t add much to the story except to advance certain plot points.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;&quot;Wind and Truth&quot; is a decent addition to the Stormlight Archive, but it doesn&apos;t reach the heights of the first two books. The shift toward deeper psychological exploration comes at the expense of the epic fantasy action and world-building that made me fall in love with the series. While I appreciate Sanderson&apos;s commitment to developing his characters&apos; inner lives, I found myself missing the balance of the earlier books.&lt;/p&gt;
&lt;p&gt;I&apos;m still invested in the series and will definitely continue reading, but I hope future installments find a better balance between character development and the grand, epic fantasy storytelling that made the Stormlight Archive special in the first place.&lt;/p&gt;
</content:encoded></item><item><title>My One Month Review of Tesla FSD: The Good, The Bad, and The Quirky</title><link>https://www.augusteo.com/blog/month-tesla-fsd-review/</link><guid isPermaLink="true">https://www.augusteo.com/blog/month-tesla-fsd-review/</guid><description>Tesla’s Full Self-Driving (FSD) is impressive, handling my daily commute hands-free 95% of the time, but it still has quirks—like occasional lane selection mistakes, red light hesitation, and aggressive yellow light behavior. Despite its flaws, I love the convenience, though my wife remains skeptical.</description><pubDate>Mon, 24 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve been using Tesla&apos;s Full Self-Driving (FSD) for about a month now, and I’ve taken every possible opportunity to test it out. Overall, I really like it—I&apos;d say it does what it needs to do about &lt;strong&gt;95% of the time&lt;/strong&gt;. But, of course, the devil is in the details.&lt;/p&gt;
&lt;p&gt;So let’s break this down into three parts: &lt;strong&gt;the good, the bad, and the things that make me go &quot;huh?&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;🚀 The Good: It’s (Mostly) Like Magic&lt;/h2&gt;
&lt;p&gt;The best way I can describe FSD right now is that it’s &lt;strong&gt;good enough to use daily without much hesitation&lt;/strong&gt;. For instance, I can drive from my home in &lt;strong&gt;San Jose to my office in San Mateo&lt;/strong&gt;—a commute that ranges from 45 minutes to 1.5 hours—&lt;strong&gt;without touching the steering wheel 95% of the time&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;That’s the key part: &lt;em&gt;&quot;hands-off 95% of the time.&quot;&lt;/em&gt; It’s not perfect, but it&apos;s really, really close.&lt;/p&gt;
&lt;p&gt;Using FSD is simple:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get in the car.&lt;/li&gt;
&lt;li&gt;Set your destination.&lt;/li&gt;
&lt;li&gt;Hold down the FSD button.&lt;/li&gt;
&lt;li&gt;Let it drive.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Tesla offers &lt;strong&gt;three driving modes&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Chill Mode&lt;/strong&gt; – Supposed to be the smoothest and most relaxed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Standard Mode&lt;/strong&gt; – A balance between caution and efficiency.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hurry Mode&lt;/strong&gt; – More aggressive, takes tighter gaps, follows cars more closely.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I mostly use &lt;strong&gt;Hurry Mode&lt;/strong&gt;, occasionally switching to Standard if it feels too reckless. I haven&apos;t really tried Chill Mode much, but maybe I should.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;🤔 The Bad: When It Just Doesn’t Get It&lt;/h2&gt;
&lt;p&gt;While FSD is impressive, it still has some &lt;em&gt;&quot;what the heck was that?&quot;&lt;/em&gt; moments. Here are the biggest issues I’ve run into:&lt;/p&gt;
&lt;h3&gt;1️⃣ Lane Selection Mistakes&lt;/h3&gt;
&lt;p&gt;One of the more common mistakes happens near my office. When turning right onto a street with multiple lanes, FSD &lt;strong&gt;sometimes picks the wrong one&lt;/strong&gt;—choosing the rightmost lane, even when it’s a right-turn-only lane. Since I actually need to go straight, I have to take over at that moment. &lt;em&gt;Not a huge deal, but annoying.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;2️⃣ The Time It Almost Ran a Red Light&lt;/h3&gt;
&lt;p&gt;This happened &lt;strong&gt;once&lt;/strong&gt;, but it was unsettling. I was waiting to turn left at a red arrow, and before the light changed, the car &lt;strong&gt;started creeping forward&lt;/strong&gt; like it wanted to go. I had to hit the brakes.&lt;/p&gt;
&lt;p&gt;It hasn’t happened again, but this is exactly why you &lt;strong&gt;still have to pay attention.&lt;/strong&gt; You never really know when FSD might do something unexpected.&lt;/p&gt;
&lt;h3&gt;3️⃣ It Doesn’t See Objects Super Close to the Car&lt;/h3&gt;
&lt;p&gt;This is an issue when parking. My driveway has a curb and a slope leading to the street. The car &lt;strong&gt;doesn’t recognize the curb&lt;/strong&gt; because it’s too close to the front cameras, so if I start FSD from a parked position, it will just &lt;strong&gt;drive straight over the curb&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Now, I &lt;strong&gt;always manually drive over the slope first&lt;/strong&gt;, then enable FSD.&lt;/p&gt;
&lt;h3&gt;4️⃣ Yellow Light Aggression&lt;/h3&gt;
&lt;p&gt;Sometimes FSD is a little &lt;em&gt;too&lt;/em&gt; eager to &lt;strong&gt;beat a yellow light&lt;/strong&gt; instead of stopping. I suspect this is because I usually have it on Hurry Mode, but even when I switch to Standard, it still tries to push through more often than I’d like.&lt;/p&gt;
&lt;h3&gt;5️⃣ Randomly Driving in the Express Lane&lt;/h3&gt;
&lt;p&gt;Even when I don’t want to use the express lane (because, you know, &lt;strong&gt;tolls&lt;/strong&gt;), FSD &lt;strong&gt;sometimes moves into it anyway&lt;/strong&gt;. Switching to Standard Mode usually makes it return to a normal lane, but not always. When it refuses to leave the express lane, I have to take over and manually correct it.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;🧐 The Quirks: Tesla’s Attempts to Keep You Engaged&lt;/h2&gt;
&lt;p&gt;Tesla &lt;strong&gt;really wants you to pay attention&lt;/strong&gt; when using FSD, and I’ve tested the limits of how it enforces this.&lt;/p&gt;
&lt;h3&gt;1️⃣ Video Calls? Nope.&lt;/h3&gt;
&lt;p&gt;I once tried to take a &lt;strong&gt;work video call&lt;/strong&gt; while driving, and Tesla immediately knew I was &lt;strong&gt;looking at my phone&lt;/strong&gt;. It gave me &lt;strong&gt;two or three warnings&lt;/strong&gt;, then &lt;strong&gt;disabled FSD completely&lt;/strong&gt; until I parked the car and restarted it. Lesson learned.&lt;/p&gt;
&lt;h3&gt;2️⃣ Closing My Eyes&lt;/h3&gt;
&lt;p&gt;I wanted to see if the car could tell if I wasn’t paying attention, so I tried &lt;strong&gt;closing my eyes&lt;/strong&gt; while my wife watched the road. Surprisingly, it didn’t immediately freak out. I didn’t time it, but I could close my eyes for a little while before it started demanding attention.&lt;/p&gt;
&lt;h3&gt;3️⃣ Sunglasses Trick&lt;/h3&gt;
&lt;p&gt;Wearing &lt;strong&gt;sunglasses&lt;/strong&gt; seems to confuse the system a bit. Since the car can’t track my eyes, it doesn’t warn me about inattention as much. Instead, it compensates by &lt;strong&gt;periodically asking me to wiggle the steering wheel&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;So theoretically—&lt;strong&gt;not saying you should do this&lt;/strong&gt;—but if someone wanted to watch a video while using FSD, they could wear sunglasses and just shake the wheel when prompted. &lt;em&gt;(Again, I am not recommending this. Just… you know… as an engineer, I was curious.)&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;🎯 Final Thoughts: Worth It?&lt;/h2&gt;
&lt;p&gt;Despite its flaws, &lt;strong&gt;I love Tesla’s FSD.&lt;/strong&gt; It has exceeded my expectations, and I plan to keep paying for it.&lt;/p&gt;
&lt;p&gt;My wife? &lt;strong&gt;Not convinced.&lt;/strong&gt; She doesn’t fully trust it, and given the occasional weird behavior, I can’t blame her. But for me, &lt;strong&gt;FSD makes commuting effortless.&lt;/strong&gt; I don’t worry about directions, I don’t stress over traffic—I just turn it on, play a podcast or audiobook, and relax.&lt;/p&gt;
&lt;p&gt;It genuinely feels like &lt;strong&gt;living in the future&lt;/strong&gt;. And I’m here for it.&lt;/p&gt;
</content:encoded></item><item><title>My Journey from a Nissan Leaf to a Tesla Model Y: An Unexpected Switch</title><link>https://www.augusteo.com/blog/leaf-to-tesla/</link><guid isPermaLink="true">https://www.augusteo.com/blog/leaf-to-tesla/</guid><description>From doubting Tesla&apos;s value to discovering it was my ideal next car, here&apos;s how creating a simple comparison spreadsheet completely changed my perspective on buying a used Model Y.</description><pubDate>Mon, 10 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve been driving my 2015 Nissan Leaf for years, but recently it&apos;s become clear that I need a replacement. The battery degradation has reached a point where I can&apos;t even reliably commute to work anymore. While it supposedly has 65 miles of range, this drops to around 55 miles on the freeway. My 30-mile commute to work eats up about 60% of the battery, and I&apos;ve developed constant range anxiety about the car dying on me mid-journey.&lt;/p&gt;
&lt;h2&gt;The Search Process&lt;/h2&gt;
&lt;p&gt;I decided to look for a used electric car since I&apos;ve already experienced the steep depreciation of buying new and didn&apos;t want to repeat that experience. My search began with opening multiple tabs across various websites like Edmunds, CarMax and Carvana, focusing on specific criteria:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Model year 2023 or newer, as I wanted something relatively recent&lt;/li&gt;
&lt;li&gt;Traffic-aware cruise control - a feature my Nissan Leaf lacked that I really wanted&lt;/li&gt;
&lt;li&gt;Sufficient cargo space to accommodate my family of four and our travel needs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/leaf-to-tesla/leaf-to-tesla-Screenshot_2025-02-08_at_4.27.37_PM.png&quot; alt=&quot;leaf-to-tesla-Screenshot_2025-02-08_at_4.27.37_PM.png&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Making an Objective Comparison&lt;/h2&gt;
&lt;p&gt;I created a comparison table of different EV options, looking at factors like price, cargo space, and range. Interestingly, this process led me to an unexpected conclusion. Despite my initial skepticism about Tesla due to concerns about build quality and what I perceived as poor value for money, the data told a different story. The used Tesla Model Y emerged as the clear winner, offering:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The highest range in its price bracket&lt;/li&gt;
&lt;li&gt;Competitive pricing (alternatives were about $10,000 more expensive)&lt;/li&gt;
&lt;li&gt;Sufficient cargo space for our needs&lt;/li&gt;
&lt;li&gt;I found one with only 2,000 miles - practically new&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Remote Purchase Experience&lt;/h2&gt;
&lt;p&gt;I ventured into new territory by deciding to purchase the car from Southern California without seeing it in person. The process involved:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A remote inspection where the dealer sent video recordings of the exterior and interior&lt;/li&gt;
&lt;li&gt;Multiple rounds of questions about the vehicle&apos;s condition&lt;/li&gt;
&lt;li&gt;A straightforward purchase process: wire the money, wait about a week for it to clear&lt;/li&gt;
&lt;li&gt;Transport arrangement that took a couple of days&lt;/li&gt;
&lt;li&gt;Delivery right to my door via a car carrier at 8 AM as scheduled&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Result&lt;/h2&gt;
&lt;p&gt;I&apos;ve been thoroughly enjoying the Tesla Model Y. The improved range and new technology have been great additions to my driving experience. While I haven&apos;t tried supercharging yet, I&apos;ve been experimenting with the Full Self-Driving feature. It&apos;s impressive, though it does have some drawbacks that I&apos;ll save for another blog post.&lt;/p&gt;
</content:encoded></item><item><title>How I Easily Turned My Voice Notes into Something Useful</title><link>https://www.augusteo.com/blog/voice-note-whisper/</link><guid isPermaLink="true">https://www.augusteo.com/blog/voice-note-whisper/</guid><description>Walking and talking helps me clear my head, but turning those scattered voice notes into something useful felt like a chore—until I found a simple Mac trick that made transcribing effortless. Now, my random thoughts turn into blog posts, reflections, and ideas faster than I can lace up my shoes.</description><pubDate>Sat, 08 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Voice notes have become an essential part of my daily routine, helping me capture thoughts and ideas while staying active. Today, I want to share my experience using the Whisper application on Mac for transcribing these voice notes, and how it compares to Apple&apos;s built-in transcription.&lt;/p&gt;
&lt;h2&gt;My Voice Notes Setup&lt;/h2&gt;
&lt;p&gt;My preferred method is simple: I take my phone on walks and use the Voice Memos app to record my thoughts. While I initially tried using AirPods, the voice quality was consistently muddled, so I switched to recording directly through my phone&apos;s microphone. This setup has proven much more reliable.&lt;/p&gt;
&lt;p&gt;Though you can record using a MacBook&apos;s microphone, I prefer the walking-and-talking approach. It combines exercise with productivity, and there&apos;s something therapeutic about voicing my thoughts while enjoying nature. The stream-of-consciousness style works well for me – no need for perfect organization at this stage.&lt;/p&gt;
&lt;h2&gt;The Transcription Process&lt;/h2&gt;
&lt;p&gt;Since I use Apple&apos;s Voice Memos app, the recordings automatically sync to my Mac. From there, the process involves a few steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Drag and drop the voice notes from the Mac app to a folder&lt;/li&gt;
&lt;li&gt;Convert the M4A files to MP3 (Whisper only accepts MP3)&lt;/li&gt;
&lt;li&gt;Run the transcription using Insanely Fast Whisper&lt;/li&gt;
&lt;li&gt;Extract just the text from the resulting JSON file&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To simplify this process, I created an alias that handles everything in one command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;alias ifwa=&apos;for f in *.m4a; do ffmpeg -i &quot;$f&quot; &quot;${f%.m4a}.mp3&quot;; done &amp;amp;&amp;amp; for f in *.mp3; do insanely-fast-whisper --device-id mps --language en --file-name &quot;$f&quot; --transcript-path &quot;${f%.mp3}.json&quot;; done &amp;amp;&amp;amp; for f in *.json; do jq -r &quot;.text&quot; &quot;$f&quot; &amp;gt; &quot;${f%.json}.txt&quot;; done &amp;amp;&amp;amp; rm *.m4a *.mp3 *.json&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Breaking Down the Script&lt;/h2&gt;
&lt;p&gt;Let&apos;s break down what this alias does step by step:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Convert M4A to MP3:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for f in *.m4a; do ffmpeg -i &quot;$f&quot; &quot;${f%.m4a}.mp3&quot;; done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This loop takes all &lt;code&gt;.m4a&lt;/code&gt; files in the folder and uses &lt;code&gt;ffmpeg&lt;/code&gt; to convert them to &lt;code&gt;.mp3&lt;/code&gt;. The &lt;code&gt;${f%.m4a}.mp3&lt;/code&gt; syntax strips the &lt;code&gt;.m4a&lt;/code&gt; extension and replaces it with &lt;code&gt;.mp3&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Run Whisper Transcription:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for f in *.mp3; do insanely-fast-whisper --device-id mps --language en --file-name &quot;$f&quot; --transcript-path &quot;${f%.mp3}.json&quot;; done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This loop processes each &lt;code&gt;.mp3&lt;/code&gt; file through the &lt;code&gt;insanely-fast-whisper&lt;/code&gt; tool. It specifies the device ID (&lt;code&gt;mps&lt;/code&gt; for Apple Silicon), sets the language to English, and outputs the transcription in a &lt;code&gt;.json&lt;/code&gt; file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Extract the Transcribed Text:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for f in *.json; do jq -r &quot;.text&quot; &quot;$f&quot; &amp;gt; &quot;${f%.json}.txt&quot;; done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, &lt;code&gt;jq&lt;/code&gt; extracts the transcribed text from each &lt;code&gt;.json&lt;/code&gt; file and saves it into a &lt;code&gt;.txt&lt;/code&gt; file. The &lt;code&gt;-r&lt;/code&gt; flag ensures the text is outputted as raw strings without quotes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Clean Up Temporary Files:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rm *.m4a *.mp3 *.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, it deletes all the original &lt;code&gt;.m4a&lt;/code&gt;, intermediate &lt;code&gt;.mp3&lt;/code&gt;, and &lt;code&gt;.json&lt;/code&gt; files, leaving only the final &lt;code&gt;.txt&lt;/code&gt; transcription files.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Beyond Just Blogging&lt;/h2&gt;
&lt;p&gt;This workflow isn&apos;t limited to blog writing. I use it for work, personal reflection, and mental clarity. There&apos;s something uniquely satisfying about talking through your thoughts as if you&apos;re having a conversation with someone.&lt;/p&gt;
&lt;h2&gt;Post-Processing&lt;/h2&gt;
&lt;p&gt;Once I have the transcription, I often feed it into an LLM like Claude or GPT to help structure the narrative. This helps transform the stream-of-consciousness recording into more organized, readable content – just like this blog post!&lt;/p&gt;
&lt;p&gt;Here’s a sample prompt:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Role&amp;gt;
Act as a professional ghostwriter &amp;amp; proofreader.

&amp;lt;Task&amp;gt;
- to rewrite my transcription in a much more clearly formatted and easy to digest format, but still in a narrative format instead of summary. 
- focus on cleaning up the rambling, but keep as much information as possible. 
- don&apos;t miss any details.
- keep the casual tone
- only write the output and nothing else
- be polite even if the transcription is angry or impolite

&amp;lt;Transcription&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I hope this workflow helps you find new ways to capture and process your thoughts. Sometimes the best ideas come when we&apos;re moving and speaking freely, rather than sitting at a desk.&lt;/p&gt;
&lt;h2&gt;Comparing Whisper and Apple Voice Notes Transcription&lt;/h2&gt;
&lt;p&gt;To understand the differences between Insanely Fast Whisper and Apple&apos;s built-in transcription, I ran the same voice note through both systems and compared the results.&lt;/p&gt;
&lt;p&gt;The left is Apple VoiceNotes transcription and the right is insanely-fast-whisper.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/voice-note-whisper/voice-note-whisper-Screenshot_2025-02-08_at_4.20.11_PM.png&quot; alt=&quot;voice-note-whisper-Screenshot_2025-02-08_at_4.20.11_PM.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Open image in new tab to see it in full size&lt;/p&gt;
&lt;h3&gt;Observations:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Accuracy:&lt;/strong&gt; Whisper clearly outperforms Apple Voice Notes in transcription accuracy. It captures complex sentences and maintains coherence, whereas Apple&apos;s transcription introduces unusual words like &quot;blockfast&quot; and &quot;Blockpus,&quot; and struggles with punctuation and sentence structure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consistency:&lt;/strong&gt; Whisper handles repeated words and filler phrases more gracefully, while Apple&apos;s transcription often misinterprets or omits them, resulting in fragmented sentences.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Usability:&lt;/strong&gt; While both methods sync well with my workflow, the cleaner and more accurate output from Whisper reduces the need for extensive post-editing, making it a more efficient tool overall.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Two Days, $95, and Endless Errors: My Painful Journey to Switch Carriers</title><link>https://www.augusteo.com/blog/porting-errors/</link><guid isPermaLink="true">https://www.augusteo.com/blog/porting-errors/</guid><description>Porting my phone number from Verizon was a frustrating mess of errors and poor service, but switching to US Mobile was a smooth and refreshing experience.</description><pubDate>Mon, 14 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;So, here’s the story of my phone number porting adventure.&lt;/p&gt;
&lt;p&gt;Last week, my phone number is still owned by Apple. To transfer it to my personal line, I had to go through a two-step process. First, I needed to port it from Apple’s business line to a personal line, and then from there, I could port it to whichever carrier I wanted. Sounds straightforward, right? Well, it wasn’t.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Transfer to Personal Line&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I opened a ticket with Apple Wireless support last week, and the wireless team got back to me within 2-3 days—not bad. Then, I got an email from Verizon saying my business line was in the process of being transferred to a personal line. It asked me to click a link to complete the process, so I did. The link took me to a form where I had to enter all sorts of personal information—address, marital status, etc. I filled everything in, hit confirm, and… error. It just kept failing with a vague error message.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/porting-errors/porting-errors-Review_everything_youve_got.png&quot; alt=&quot;porting-errors-Review_everything_youve_got.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Very helpful error.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phone Call Fiasco&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Frustrated, I called Verizon. The rep asked me the same questions I’d already answered online (don’t you love that?) and then told me the port was stuck. I couldn’t complete it over the phone either because there was a “pending port.” He opened a ticket internally and promised to call me back once it was sorted out. Oh, and I asked if I needed to wait a certain amount of time after the transfer to a personal line before porting to another carrier. Apparently, I’d need to wait 30 days.&lt;/p&gt;
&lt;p&gt;Of course, I never heard back from him. So, the next day, I called again. This time, I got a different person, and we finally managed to do the transfer over the phone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Verizon App Disaster&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Once my line was on a personal plan, I downloaded the Verizon app to check my plan. They didn’t ask me what plan I wanted, so I figured I’d better check it myself. I opened the app, and here’s where things got ridiculous. The app was clearly broken—screens were full of placeholder text. It looked like the developers never tested it. Just… wow. Amazing quality control, Verizon.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/porting-errors/porting-errors-Details.jpeg&quot; alt=&quot;porting-errors-Details.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Don&apos;t you love those placeholders?&lt;/p&gt;
&lt;p&gt;A couple of days later, I got a message saying I was almost out of data, even though I’d barely used the phone. So, I decided to say “screw it” and initiated the transfer to US Mobile, which is the carrier I wanted from the start.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/porting-errors/porting-errors-Today_345_PM.jpg&quot; alt=&quot;porting-errors-Today_345_PM.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Billing Nightmare&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Of course, I still owed Verizon $95 for two days of service. I had no problem paying it if it meant I’d never have to deal with them again. But—surprise, surprise—it wasn’t that easy. When I went to add a new payment method in the Verizon app, the button didn’t work. I clicked on the dropdown to select a credit or debit card, and nothing happened. It was just dead.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/porting-errors/porting-errors-Make_a_payment_today..png&quot; alt=&quot;porting-errors-Make_a_payment_today..png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You would think you can add a new credit card here, right?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/porting-errors/porting-errors-Add_a_new_payment_method..png&quot; alt=&quot;porting-errors-Add_a_new_payment_method..png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Selecting ‘Add a credit card’ does nothing&lt;/p&gt;
&lt;p&gt;I had to go into account settings, manually add a payment method, then go back to the pay bill screen to finally settle up. Every single step with Verizon was a mess, whether it was the process itself or their terrible software.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Enter US Mobile: A Breath of Fresh Air&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In contrast, porting my number to US Mobile was a breeze. Minimal clicks, all the information I needed was right there, and everything just worked. It was such a refreshing experience after dealing with Verizon’s chaos.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/porting-errors/porting-errors-270.00.png&quot; alt=&quot;porting-errors-270.00.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Moral of the Story&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This whole saga feels like a David vs. Goliath tale. Verizon, the Goliath, is this massive, bloated, and inefficient giant. Meanwhile, US Mobile, the David in this scenario, is nimble, smart, and focused. I think David’s going to win—at least, I know I’m rooting for him.&lt;/p&gt;
&lt;p&gt;So yeah, if you’re thinking of switching carriers, take it from me: go with the little guy. You’ll save yourself a lot of headaches.&lt;/p&gt;
</content:encoded></item><item><title>From Flappy Dove to TF Math: Our Incredible Cambrian Code Off Journey</title><link>https://www.augusteo.com/blog/code-off/</link><guid isPermaLink="true">https://www.augusteo.com/blog/code-off/</guid><description>Our family had an unforgettable experience at the Cambrian Code Off</description><pubDate>Fri, 03 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/code-off/code-off-F16DFA98-366F-4788-AA6C-6BD33777B68A.jpeg&quot; alt=&quot;code-off-F16DFA98-366F-4788-AA6C-6BD33777B68A.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Dear Grace and George,&lt;/p&gt;
&lt;p&gt;Remember a couple of weeks ago when we went to the Cambrian Code Off together? It was so much fun! We got to be a family team, which was really cool because Dad could help you out.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/code-off/code-off-IMG_4759.jpeg&quot; alt=&quot;code-off-IMG_4759.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When we got there, we found a seat and waited for everything to start. Some big kids told us that the theme was &quot;education,&quot; but you know what? We didn&apos;t really care about that. Grace, you wanted to make a game like Flappy Bird, so that&apos;s what we did! We found a nice and easy example on the Scratch website, and you changed the code to make it your own. You called it &quot;&lt;strong&gt;Flappy Dove,&lt;/strong&gt;&quot; and it had a cute little dove flying and trying not to hit the pipes.&lt;/p&gt;
&lt;p&gt;Then, we added a bunch of funny animals as obstacles. First, we put in a dinosaur, and we learned how to flip the pictures and make the dinosaur&apos;s mouth face the right way. We even made copies of the dinosaur and flipped them around. After that, you added a frog that shot its tongue out to catch a fly. The game had some silly problems, but it worked, and we had a blast making it!&lt;/p&gt;
&lt;p&gt;&amp;lt;aside&amp;gt;
💡 Press ‘Spacebar’ to flap your wings.&lt;/p&gt;
&lt;p&gt;&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://scratch.mit.edu/projects/1005024439/embed&quot;&gt;https://scratch.mit.edu/projects/1005024439/embed&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;While we were working on the game, Mom kept messaging me, saying that we should do something about education. I told her that I didn&apos;t mind if we didn&apos;t do education stuff because I just wanted you to have fun learning. But you know how Mom is – we can&apos;t ignore her! She had this idea that we could change the Flappy Dove game to be about a girl trying to get to school while avoiding obstacles. Grace, you said a girl can&apos;t flap, and when I suggested using the dove, you said, &quot;The dove doesn&apos;t need to go to school. It&apos;s a bird!&quot;&lt;/p&gt;
&lt;p&gt;I asked an AI to give us some ideas for education games on Scratch, and one of them was about math. We searched the Scratch website and found a cool project that used Yoshi and Mario. There were math problems, and you had to choose the right answer by moving Yoshi with the arrow keys.&lt;/p&gt;
&lt;p&gt;That&apos;s when you had your brilliant idea, Grace! You said we should make a true/false game, and I loved it! We quickly made a blackboard background with true and false buttons on each side. You added a cat and figured out how to make the cat move with the mouse all by yourself. I was so impressed!&lt;/p&gt;
&lt;p&gt;I copied the number system from the Yoshi game and made it so that the game would show addition problems with random numbers. The player could move their mouse to click on the true or false button. You added the scoreboard and put it right in the middle. It was perfect!&lt;/p&gt;
&lt;p&gt;You named it “&lt;strong&gt;TF Math&lt;/strong&gt;”.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://scratch.mit.edu/projects/1005041877/embed&quot;&gt;https://scratch.mit.edu/projects/1005041877/embed&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We only had two hours to make our games, and we spent 90 minutes on the Flappy Dove game and looking for ideas. But the math game only took us 30 minutes! It was exciting and a little scary because we didn&apos;t have much time. Grace, you kept telling me how much time we had left, but I told you not to worry because I knew we would finish it. And we did!&lt;/p&gt;
&lt;p&gt;Mom and George came to join us after his baseball game, and we showed her the game. Then, the judges came to look at our game. While we were waiting for the judging to end, we played some other fun games on Scratch. Grace, do you remember Stella? The girl sitting at our desk? She came over and played the Pikachu volleyball game with me!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/code-off/code-off-3caec4d7-5d04-4a12-8f04-d12a9d38d8be.png&quot; alt=&quot;code-off-3caec4d7-5d04-4a12-8f04-d12a9d38d8be.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When the judging was over, the organizers started giving out prizes. They showed us the prizes first, and there was a super cool coding robot. You said you really wanted it. Guess what? When they called out the winner, it was our ticket number! Both of you ran to the front to get the robot. George, you grabbed it and ran back to us so fast! The organizer had to stop you two to take a picture. It was amazing! You wanted the robot, and you got it. And George, you helped out with your quick moves.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/code-off/code-off-F6A79621-52DC-4C57-84D5-1FF1F130CF7F.jpeg&quot; alt=&quot;code-off-F6A79621-52DC-4C57-84D5-1FF1F130CF7F.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Finally, they announced the winners for the individual, team, and family categories. They said who won second place, and it was Stella and her dad! We cheered for them. Then, they called out the first-place winner, and it was us! They said that our game looked like something people would really enjoy playing. Grace, you ran up to the front to get the trophy and the certificate. The trophy was shaped like a light bulb, and it said that you are a &quot;Luminaries of Logic.&quot; You were so happy, and we celebrated with Stella and took some pictures together.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/code-off/code-off-F2C53B51-280B-4CEA-B411-187E2645CA98.jpeg&quot; alt=&quot;code-off-F2C53B51-280B-4CEA-B411-187E2645CA98.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We came to the Cambrian Code Off, had an incredible time, and won both the raffle and the game. It was a day we&apos;ll never forget!&lt;/p&gt;
</content:encoded></item><item><title>How Emotions are Made: The Secret Life of the Brain - Book Summary</title><link>https://www.augusteo.com/blog/how-emotions-are-made-summary/</link><guid isPermaLink="true">https://www.augusteo.com/blog/how-emotions-are-made-summary/</guid><description>How Emotions are Made: The Secret Life of the Brain - Book Summary - a blog post by Victor Augusteo</description><pubDate>Thu, 30 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/how-emotions-are-made-summary/how-emotions-are-made-summary-Untitled.png&quot; alt=&quot;how-emotions-are-made-summary-Untitled.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&quot;How Emotions are Made: The Secret Life of the Brain&quot; is a book by psychologist and neuroscientist Lisa Feldman Barrett. In this book, Barrett presents a new theory of emotions, arguing that they are not hardwired into our brains, but rather, are constructed by our brains based on our experiences and the cultural context.&lt;/p&gt;
&lt;h3&gt;Chapter 1: Looking for Emotion&apos;s &quot;Fingerprints&quot;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Barrett questions the old-school view that emotions are hardwired into our brains and universal across all humans.&lt;/li&gt;
&lt;li&gt;She highlights that research hasn&apos;t been able to find specific brain activity patterns (or &quot;fingerprints&quot;) for each emotion.&lt;/li&gt;
&lt;li&gt;For example, people from different cultures might not display or recognize emotions in the same way, which makes us question the universality of emotional expressions.&lt;/li&gt;
&lt;li&gt;Instead, she suggests that emotions are constructed by our brains based on our past experiences and the stuff we&apos;ve learned.&lt;/li&gt;
&lt;li&gt;So, think of how we might learn to associate a smile with happiness based on our experiences, rather than being born with that knowledge.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Chapter 2: Emotions are Made, Not Born&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Barrett explains that our brains create emotions based on our experiences and the cultural context we live in.&lt;/li&gt;
&lt;li&gt;So, instead of having pre-programmed emotions in our brains, we learn to feel and express emotions based on our upbringing and what&apos;s considered &quot;normal&quot; in our culture.&lt;/li&gt;
&lt;li&gt;Like how in some cultures, people might express grief by wailing loudly, while in others, they might remain silent and stoic.&lt;/li&gt;
&lt;li&gt;This idea of constructed emotions helps explain why emotions are experienced and expressed differently across individuals and cultures.&lt;/li&gt;
&lt;li&gt;Basically, our brains are like emotion-building machines that use our experiences and the stuff we&apos;ve learned to create emotions in different situations.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Chapter 3: The Myth of the Emotional Brain&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Barrett busts the myth that there are dedicated brain regions for specific emotions (like a &quot;fear center&quot; or a &quot;happiness center&quot;).&lt;/li&gt;
&lt;li&gt;She shows that research hasn&apos;t found consistent brain regions that are responsible for specific emotions.&lt;/li&gt;
&lt;li&gt;Instead, our brains use networks of different areas to create emotions, kind of like a team effort.&lt;/li&gt;
&lt;li&gt;So, it&apos;s like how you might use different ingredients to cook a dish, but no single ingredient is solely responsible for the final result.&lt;/li&gt;
&lt;li&gt;In conclusion, emotions aren&apos;t neatly organized in our brains, but rather constructed through complex interactions between different brain areas.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Chapter 4: A Fresh Look at Human Nature&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Barrett argues that this new view on emotions challenges the traditional idea that humans are governed by innate emotional instincts.&lt;/li&gt;
&lt;li&gt;By understanding that emotions are constructed, we can better grasp how people experience and express emotions differently.&lt;/li&gt;
&lt;li&gt;This new perspective also helps explain the diversity of emotional experiences across people and cultures.&lt;/li&gt;
&lt;li&gt;For instance, imagine how someone who grew up in a culture that values emotional expression might react differently to a situation compared to someone from a culture that values emotional restraint.&lt;/li&gt;
&lt;li&gt;The bottom line is that our emotional experiences are shaped by our brains, which use our past experiences and learned concepts to create emotions in different situations.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Chapter 5: Emotions and Health&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Barrett explores how emotions are linked to our physical health, like how stress can affect our immune system and make us more prone to illnesses.&lt;/li&gt;
&lt;li&gt;She explains that our brains construct emotions based on sensations from our bodies, which can impact our health and well-being.&lt;/li&gt;
&lt;li&gt;For example, if we constantly feel stressed and anxious, our bodies might be more susceptible to inflammation and chronic diseases.&lt;/li&gt;
&lt;li&gt;So, learning how to manage our emotions effectively can actually have a positive impact on our overall health.&lt;/li&gt;
&lt;li&gt;It&apos;s like how practicing meditation or other relaxation techniques can help us reduce stress and improve our well-being.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Chapter 6: Mastering Your Emotions&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Barrett emphasizes the importance of being able to accurately differentiate and label our emotions, which she calls &quot;emotional granularity.&quot;&lt;/li&gt;
&lt;li&gt;This skill can help us manage our emotions better, make smarter decisions, and improve our well-being.&lt;/li&gt;
&lt;li&gt;For example, if we can recognize that we&apos;re feeling frustrated rather than just &quot;upset,&quot; we can take more targeted steps to address the situation.&lt;/li&gt;
&lt;li&gt;She suggests techniques like mindfulness, journaling, and learning new emotion words to improve our emotional granularity.&lt;/li&gt;
&lt;li&gt;So, by understanding that emotions are constructed, we can become more in control of our emotional lives and better equipped to handle whatever life throws our way.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Chapter 7: Emotions and the Law&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Barrett looks at how our understanding of emotions can impact the legal system, especially when it comes to eyewitness testimony and lie detection.&lt;/li&gt;
&lt;li&gt;She discusses how emotions can influence our memories and perceptions, which could lead to biases and inaccuracies in eyewitness accounts.&lt;/li&gt;
&lt;li&gt;For example, imagine being an eyewitness to a crime and feeling scared. Your fear might distort your memory, causing you to misremember certain details.&lt;/li&gt;
&lt;li&gt;She also critiques methods like polygraph tests, saying that they&apos;re based on flawed assumptions about emotions and their connection to lying.&lt;/li&gt;
&lt;li&gt;In the end, she highlights how understanding constructed emotions could help create better legal procedures and more accurate decision-making.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Chapter 8: Emotions in the Digital Age&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Barrett examines how technology, like social media and digital communication, affects our emotional lives.&lt;/li&gt;
&lt;li&gt;She talks about the limitations of digital communication when it comes to conveying emotions, which can lead to misunderstandings and misinterpretations.&lt;/li&gt;
&lt;li&gt;Think of how a text message might be taken the wrong way if the recipient can&apos;t pick up on the sender&apos;s intended emotion.&lt;/li&gt;
&lt;li&gt;She also looks at how emojis and other digital tools can help express emotions more accurately in online communication.&lt;/li&gt;
&lt;li&gt;Overall, the chapter discusses the challenges and opportunities of navigating our emotions in an increasingly digital world.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Chapter 9: Building a Better Brain&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Barrett wraps up the book by summarizing the main insights from the theory of constructed emotion and their implications for improving our well-being.&lt;/li&gt;
&lt;li&gt;She highlights the importance of developing emotional granularity and effective emotional regulation for better mental and physical health.&lt;/li&gt;
&lt;li&gt;By incorporating these ideas into education, therapy, and mental health interventions, we can potentially help people lead happier and healthier lives.&lt;/li&gt;
&lt;li&gt;She also emphasizes the need to understand cultural differences in emotional experiences and expressions, as well as the impact of technology on our emotional lives.&lt;/li&gt;
&lt;li&gt;In the end, the book encourages us to apply these insights to our own lives, empowering us to take control of our emotional experiences and build better brains.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope you found it as interesting as I did.&lt;/p&gt;
&lt;p&gt;Remember, our brains construct our emotions, which means we have the power to shape and control our emotional lives!&lt;/p&gt;
</content:encoded></item><item><title>When ChatGPT Goes Wrong - 3 Examples</title><link>https://www.augusteo.com/blog/chatgpt-fails-task/</link><guid isPermaLink="true">https://www.augusteo.com/blog/chatgpt-fails-task/</guid><description>ChatGPT has some limitations that can result in silly mistakes and nonsensical outputs.</description><pubDate>Mon, 12 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We have seen tons of impressive examples of how ChatGPT wows and bedazzles with its seemingly superhuman knowledge and writing ability.&lt;/p&gt;
&lt;p&gt;But let’s take a look at the other side and see how it can fail just as spectacularly.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Task 1: Comparing Bed Sizes&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;The AI does not understand numbers the way we do. It treats numbers like words, and as a result, makes silly mistakes that my 5-year-old could call out.&lt;/p&gt;
&lt;p&gt;For example, when asked to compare bed sizes, the AI may say that 108 inches is narrower than 76 inches, which is clearly not true.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/chatgpt-fails-task/chatgpt-fails-task-Screenshot_2022-12-11_at_10.13.44_PM.png&quot; alt=&quot;chatgpt-fails-task-Screenshot_2022-12-11_at_10.13.44_PM.png&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Task 2: Making a Topical Dad Joke&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;I planned to go to Tahoe last weekend but had to cancel because of the impending snow storm and avalanche risk. So I asked the AI to make a joke about it.&lt;/p&gt;
&lt;p&gt;Unfortunately, the AI made a spurious connection between snowmen and avalanches, and then it hallucinated the double meaning of blizzard. The result was a joke that made no sense.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/chatgpt-fails-task/chatgpt-fails-task-Screenshot_2022-12-09_at_3.31.54_PM.png&quot; alt=&quot;chatgpt-fails-task-Screenshot_2022-12-09_at_3.31.54_PM.png&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Task 3: Writing Python Code to Solve Titanic Dataset with Conformal Predictions&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;When asked to solve the famous Titanic problem with logistic regression, the AI was able to generate a pretty plausible code.&lt;/p&gt;
&lt;p&gt;As a follow-up, I asked it to use conformal prediction instead of logistic regression, and it started to hallucinate.&lt;/p&gt;
&lt;p&gt;ChatGPT suggested that we import &lt;a href=&quot;https://pypi.org/project/scikit-CP/&quot;&gt;scikit-cp&lt;/a&gt;, which stands for Computational Physics, not Conformal Prediction. It looks like it just made the assumption that the package could solve the problem and wrote the code without understanding what it was doing.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/chatgpt-fails-task/chatgpt-fails-task-Screenshot_2022-12-12_at_10.56.14_AM.png&quot; alt=&quot;chatgpt-fails-task-Screenshot_2022-12-12_at_10.56.14_AM.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/chatgpt-fails-task/chatgpt-fails-task-Screenshot_2022-12-12_at_11.02.16_AM.png&quot; alt=&quot;chatgpt-fails-task-Screenshot_2022-12-12_at_11.02.16_AM.png&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;In conclusion, ChatGPT is a powerful tool that can generate impressive results, but it is not perfect. It has some limitations that can result in silly mistakes and nonsensical outputs. Some of these limitations include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The AI does not understand numbers the way humans do, and can make mistakes when comparing sizes or quantities.&lt;/li&gt;
&lt;li&gt;It can make spurious connections and hallucinations when asked to generate jokes or creative content.&lt;/li&gt;
&lt;li&gt;It may import the wrong packages or use the wrong algorithms when asked to write code, if it does not understand the task properly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Despite these limitations, ChatGPT is still an impressive tool that can be used for many useful applications. It just needs to be used with care and understanding of its limitations.&lt;/p&gt;
</content:encoded></item><item><title>Zebra Floss Poem</title><link>https://www.augusteo.com/blog/zebra-floss-poem/</link><guid isPermaLink="true">https://www.augusteo.com/blog/zebra-floss-poem/</guid><description>Zebras must floss their teeth to maintain good dental health</description><pubDate>Mon, 05 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/zebra-floss-poem/zebra-floss-poem-IMG_6019.jpeg&quot; alt=&quot;zebra-floss-poem-IMG_6019.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Zebras with stripes so bold and bright
Are known for their speed and their sight
But there&apos;s something they often neglect
Their dental care is often a suspect&lt;/p&gt;
&lt;p&gt;They roam the savannah, wild and free
But their teeth can get quite cavitied
So they must take the time to floss
To keep their mouth healthy, no matter the cost&lt;/p&gt;
&lt;p&gt;With their tough, fibrous grasses to chew
Their teeth can get quite yellow and blue
But with some floss, they can keep them clean
And avoid any dental scenes&lt;/p&gt;
&lt;p&gt;So let&apos;s all take a lesson from the zebra herd
And make sure to floss, it&apos;s not just a word
Your teeth will thank you, I guarantee
Flossing is important, as zebras can see!&lt;/p&gt;
&lt;p&gt;—&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Poem written with the help of ChatGPT&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Zebra photo by yours truly&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>Moving From Markdown+Hugo to Notion+NextJS</title><link>https://www.augusteo.com/blog/move-notion-nextjs/</link><guid isPermaLink="true">https://www.augusteo.com/blog/move-notion-nextjs/</guid><description>After years of using markdown and Hugo, I decided to switch to using Notion and NextJS for writing and publishing my website.</description><pubDate>Fri, 02 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;After years of writing my blog in markdown and publishing it with Hugo, I have recently decided to switch to using Notion and NextJS. In this blog post, I want to share my experience and explain why I made this switch.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Pain Point&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;One of the biggest pain points with using markdown and Hugo was that I couldn&apos;t easily add images to my blog posts. I had to upload the images separately and then insert them into my markdown files using the appropriate syntax. This was time-consuming and painful, and it often resulted in broken links and missing images. With Notion, I can simply drag and drop an image into my blog post, and it will be automatically uploaded and inserted into the right place. This makes it much easier and more intuitive to add images to my blog posts.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Why Notion&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;One of the main reasons I decided to switch to Notion is because of its rich formatting options. Unlike markdown, which is a simple and plain text-based format, Notion allows me to add formatting, images, videos, and other rich media to my blog posts. This makes my writing more engaging and visually appealing, and allows me to express my ideas in a more creative and effective way.&lt;/p&gt;
&lt;p&gt;Another advantage of using Notion is its ability to collaborate with other people. With markdown, I had to send my blog posts to other people as plain text files, which they would then have to edit and send back to me. This was a cumbersome and error-prone process. With Notion, I can share my blog posts with other people and work on them together in real-time. This makes it easier for me to get feedback and suggestions from other writers, and to improve the quality of my blog posts.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Why NextJS&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;In addition to the benefits of using Notion for writing, I also decided to switch to NextJS for publishing my blog.&lt;/p&gt;
&lt;p&gt;One of the coolest features of NextJS is its support for &lt;strong&gt;Incremental Static Regeneration (ISR)&lt;/strong&gt;. This means that when I write a new blog post in Notion, NextJS will automatically detect the change and update the corresponding static page on my blog without me having to manually push a new commit to Git and rebuild the website. This allows me to write and publish blog posts quickly and easily, without having to worry about the technical details of building and deploying my blog.&lt;/p&gt;
&lt;p&gt;Craigary on GitHub has a great template to get started: &lt;a href=&quot;https://github.com/craigary/nobelium&quot;&gt;Nobelium&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Migration Challenge&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;I have not finished migrating all of my old blog posts yet, so for now, they are still available on my subdomain &lt;a href=&quot;https://hugo.augusteo.com/&quot;&gt;&lt;strong&gt;https://hugo.augusteo.com&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Moving from markdown and Hugo to Notion and NextJS was not without challenges.&lt;/p&gt;
&lt;p&gt;The biggest challenge I am facing now is migrating my old blog posts from markdown to Notion.&lt;/p&gt;
&lt;p&gt;This involves converting the markdown files to Notion pages, preserving the formatting and images, and organizing them into a logical structure.&lt;/p&gt;
&lt;p&gt;Let’s see how it goes.&lt;/p&gt;
</content:encoded></item><item><title>Walkable City by Jeff Speck - Book Club Discussion Points</title><link>https://www.augusteo.com/blog/walkable-city/</link><guid isPermaLink="true">https://www.augusteo.com/blog/walkable-city/</guid><description>Walkable City by Jeff Speck - Book Club Discussion Points - a blog post by Victor Augusteo</description><pubDate>Thu, 06 May 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/walkable-city/walkable-city-albert-dera-3A4YNbpn9Sc-unsplash_1.jpg&quot; alt=&quot;walkable-city-albert-dera-3A4YNbpn9Sc-unsplash_1.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;My book club at work will discuss this book, and I’m sure it will be an exciting session.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Does this book convince you that a walkable city is something that every city should strive to be?&lt;/li&gt;
&lt;li&gt;Do you currently drive a car? What would it take for you to ditch your car?&lt;/li&gt;
&lt;li&gt;The author is against traffic study and believes that any increased supply will quickly be outpaced by increased demand. Do you agree with this?&lt;/li&gt;
&lt;li&gt;Speck wrote very highly of the parking guru Donald Shoup and his 80% target occupancy rate. Why do you think it is?&lt;/li&gt;
&lt;li&gt;Why did Speck recommend private parties to help pay for public transport? Do you agree with it?&lt;/li&gt;
&lt;li&gt;Speck wrote, “nobody drove dangerously through this intersection, precisely because the intersection felt dangerous.” Would you support making your intersection feels more dangerous to reduce accidents potentially?&lt;/li&gt;
&lt;li&gt;What was the most walkable city that you’ve ever visited? Does it map to Speck’s point about “Pedestrians need to feel safe and comfortable, but they also need to be entertained.”?&lt;/li&gt;
&lt;li&gt;In the end, we need to pick our winners. Where can spending the least money make the most difference in your city?&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>How to Build Responsive Newsletter Signup Box for Hugo</title><link>https://www.augusteo.com/blog/newsletter-hugo/</link><guid isPermaLink="true">https://www.augusteo.com/blog/newsletter-hugo/</guid><description>How to Build Responsive Newsletter Signup Box for Hugo - a blog post by Victor Augusteo</description><pubDate>Mon, 03 May 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/newsletter-hugo/newsletter-hugo-202105rinck-content-studio-rmubashufwo-unsplash_2.jpg&quot; alt=&quot;newsletter-hugo-202105rinck-content-studio-rmubashufwo-unsplash_2.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This blog post focuses on Hugo, but you can use the same technique for other static site generators.&lt;/p&gt;
&lt;p&gt;Starting last week, I moved my newsletter hosting from Moosend to Revue. Moosend came with its newsletter sign-up box with its builder. Revue does not. So I thought to create one by myself.&lt;/p&gt;
&lt;p&gt;How hard can it be?&lt;/p&gt;
&lt;p&gt;I started by just searching for a simple subscription box template on the internet and found this one. It has the structure that I wanted.&lt;/p&gt;
&lt;p&gt;We will use that as the base and change it to suit our needs.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make it responsive&lt;/li&gt;
&lt;li&gt;Change the color palette&lt;/li&gt;
&lt;li&gt;Implement the change on Hugo blog with partials&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Making it responsive&lt;/h2&gt;
&lt;p&gt;To make it responsive, all I had to do was change:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;form {
    width: 450px;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;form {
    width: 80%;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Choosing color&lt;/h2&gt;
&lt;p&gt;I used the &lt;a href=&quot;https://www.canva.com/colors/color-palettes/mermaid-lagoon/&quot;&gt;Mermaid Lagoon color palette&lt;/a&gt; from Canva.&lt;/p&gt;
&lt;p&gt;If you don’t like this one, you can just pick another from &lt;a href=&quot;https://www.canva.com/colors/color-palettes/&quot;&gt;the list&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are two places to change: the main embed and the submit button.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.revue-embed {
    background: #0c2d48;
}

#member_submit {
    background: #2e8bc0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Hugo implementation&lt;/h2&gt;
&lt;p&gt;I’m using &lt;code&gt;cocoa-eh&lt;/code&gt; as my theme, so please change your theme path accordingly.&lt;/p&gt;
&lt;p&gt;First, we need to create a partial file to contain our form code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;site/themes/cocoa-eh/layouts/partials/newsletter_signup.html

&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Newsletter Signup --&amp;gt;
&amp;lt;style type=&quot;text/css&quot; media=&quot;screen&quot;&amp;gt;
    {{ partial &quot;css/newsletter.css&quot; . | safeCSS }}
&amp;lt;/style&amp;gt;
&amp;lt;link href=&apos;https://fonts.googleapis.com/css?family=Lato:400,700&apos; rel=&apos;stylesheet&apos; type=&apos;text/css&apos;&amp;gt;

&amp;lt;div id=&quot;revue-embed&quot; class=&quot;revue-embed&quot;&amp;gt;
    &amp;lt;form action=&quot;http://digest.augusteo.com/add_subscriber&quot; method=&quot;post&quot; id=&quot;revue-form&quot; name=&quot;revue-form&quot;
          target=&quot;_blank&quot;&amp;gt;
        &amp;lt;div class=&quot;revue-header&quot;&amp;gt;
            &amp;lt;p&amp;gt;Get your free weekly digest&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class=&quot;revue-description&quot;&amp;gt;
            &amp;lt;p&amp;gt;Once a week, I will send you a highly curated weekly digest on:&amp;lt;/p&amp;gt;
            &amp;lt;br&amp;gt;
            &amp;lt;p&amp;gt;Science/Technology, Software/AI, Business/Finance, and Culture/Fun.&amp;lt;/p&amp;gt;
            &amp;lt;br&amp;gt;
            &amp;lt;p&amp;gt;The easiest way to learn the most salient content every week.&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class=&quot;revue-input&quot;&amp;gt;
            &amp;lt;input type=&quot;email&quot; class=&quot;button&quot; name=&quot;member[email]&quot; id=&quot;member_email&quot; placeholder=&quot;Your email address...&quot;&amp;gt;
            &amp;lt;input type=&quot;submit&quot; class=&quot;button&quot; name=&quot;member[subscribe]&quot; id=&quot;member_submit&quot; value=&quot;Subscribe&quot;&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/form&amp;gt;
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we need to create css file for the styling.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;site/themes/cocoa-eh/layouts/partials/css/newsletter.css

&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;.revue-embed {
    background: #0c2d48;
    font-family: &apos;Lato&apos;, sans-serif;
    color: #FDFCFB;
    text-align: center;
    border-radius: 10px;
}

form {
    width: 80%;
    margin: 0.5em auto;
}

.revue-header {
    font-size: 26px;
    text-transform: uppercase;
    letter-spacing: 2px;
    padding-top: 0.5em;
    padding-bottom: 1em;
}

.revue-description {
    font-size: 15px;
    letter-spacing: 1px;
    line-height: 1.3em;
    margin: -2px 0 45px;
}

.revue-input {
    display: flex;
    align-items: center;
    padding-bottom: 2em;
}

.button {
    height: 44px;
    border: none;
}

#member_email {
    width: 65%;
    font-size: 14px;
    background: #FDFCFB;
    font-family: inherit;
    color: #737373;
    letter-spacing: 1px;
    text-indent: 5%;
    border-radius: 5px 0 0 5px;
}

#member_submit {
    width: 35%;
    height: 46px;
    font-size: 14px;
    background: #2e8bc0;
    font-family: inherit;
    font-weight: bold;
    color: inherit;
    letter-spacing: 1px;
    border-radius: 0 5px 5px 0;
    cursor: pointer;
    transition: background .3s ease-in-out;
}

#member_submit:hover {
    background: #d45d7d;
}

input:focus {
    outline: none;
    outline: 2px solid #E86C8D;
    box-shadow: 0 0 2px #E86C8D;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once that’s done, all we have to do is to add the partials where we want the box to show up.&lt;/p&gt;
&lt;p&gt;For example, I want the box to show up at the bottom of every blog post. So we need to add this partial:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{{ partial &quot;newsletter_signup.html&quot; . }}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To just below the blog post on:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;site/themes/cocoa-eh/layouts/blog/single.html

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s it. Just run the server and verify that it works!&lt;/p&gt;
</content:encoded></item><item><title>Weekly Digest 15 - HQ Audio Makes You Smarter, NFT Gambling, FLoC, and Soft Robots</title><link>https://www.augusteo.com/blog/weekly-digest-15/</link><guid isPermaLink="true">https://www.augusteo.com/blog/weekly-digest-15/</guid><description>Weekly Digest 15 - HQ Audio Makes You Smarter, NFT Gambling, FLoC, and Soft Robots - a blog post by Victor Augusteo</description><pubDate>Sun, 18 Apr 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/weekly-digest-15/weekly-digest-15-202104chris-bemmerl-xjwgh5hg4pi-unsplash-1.jpg&quot; alt=&quot;weekly-digest-15-202104chris-bemmerl-xjwgh5hg4pi-unsplash-1.jpg&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Must Read&lt;/h2&gt;
&lt;p&gt;Bad quality audio makes us think less of the speaker and what they’re saying. Ensure your audio is high quality in calls, online meetings, interviews, podcasts, or any other voice recording. When audio quality is high (vs low), people judge the content as better and more important. They also judge the speaker as more intelligent, competent, and likable.&lt;/p&gt;
&lt;p&gt;I bought a &lt;a href=&quot;https://www.bluemic.com/en-us/products/yeti/&quot;&gt;Blue Yeti&lt;/a&gt; microphone and it had served me well throughout the pandemic.&lt;/p&gt;
&lt;h2&gt;Science &amp;amp; Technology&lt;/h2&gt;
&lt;p&gt;The study recruited three groups of students who took one of the two online courses. All participants took the courses between August 2018 and August 2020. The authors examined the participants’ well being both before and after taking one of the two courses. To do so, they used a psychometric self-report measure called the &lt;a href=&quot;https://www.internationaljournalofwellbeing.org/index.php/ijow/article/view/526&quot;&gt;PERMA Profiler&lt;/a&gt;. In terms of their overall average score on the 15-item measure, the “science of well being” students went from a baseline average of about 6.6 to an average of 7.7 after completing the course. The “introduction to psychology” students, on the other hand, went from a baseline of 6.7 to a post-course average of about 7.3. Still an improvement, but a significantly smaller improvement.&lt;/p&gt;
&lt;p&gt;By doing video surveillance deep in the ocean, researchers working in the North Pacific have discovered the densest accumulation of plastic waste ever recorded on an abyssal seafloor, finding that the majority of this waste is single-use packaging.&lt;/p&gt;
&lt;p&gt;This self-extending soft robot has applications to archaeology, space exploration, and search and rescue — with a simple elegant design inspired by a plant.&lt;/p&gt;
&lt;p&gt;Scientists in the US have developed a paint significantly “whiter than the whitest paint currently available”. Tests carried out by researchers at Purdue University on their “ultra-white” paint showed it reflected more than 98% of sunlight. That suggests, the scientists say, that it could help save energy and fight climate change.&lt;/p&gt;
&lt;p&gt;Some experts say tooth-grinding is a behavior rather than a disorder, and the dentist’s chair isn’t the best place to address it.&lt;/p&gt;
&lt;h2&gt;Business &amp;amp; Technology&lt;/h2&gt;
&lt;p&gt;Goodreads enjoys a monopoly in the space of book social networks. Looking at data from Similarweb, Goodreads records 95mn visits per month, whereas its closest competitor, LibraryThing, is less than 1.5mn visits per month. The biggest moat is their superiority in organic search.&lt;/p&gt;
&lt;p&gt;According to an internal memo published by BuzzFeed, Facebook plans to build “a version of Instagram that allows people under the age of 13 to safely use Instagram for the first time. Instagram, in particular, exploits young people’s fear of missing out and desire for peer approval to encourage children and teens to constantly check their devices and share photos with their followers. The platform’s relentless focus on appearance, self-presentation, and branding presents challenges to adolescents’ privacy and wellbeing.&lt;/p&gt;
&lt;p&gt;Welcome to the non-fungible, memeified, cryptodenominated, degenerate future of finance. What is happening? It’s tempting to blame the money mutations of the past year on the coronavirus and to see the financial bets on Robinhood and the soaring prices of NFTs as the anguished outcries of the bored, locked down, and anhedonic. Roiling under this familiar surface is the reverberation of a much larger, fundamental shift: Something is changing in how we think about money. Maybe the question we should have been texting our financially literate friends wasn’t “What is an NFT?” but “What is money now?”&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bitclout.com/one_pager.pdf&quot;&gt;BitClout&lt;/a&gt; is pitching itself as a “crypto celebrity network.” The site offers consumers the opportunity to purchase tokens tied to the identities of 15,000 influential Twitter accounts, including the likes of Elon Musk and Katy Perry—albeit without their permission.&lt;/p&gt;
&lt;p&gt;In the space of a week, BitClout has reportedly earned $160 million and attracted the interest of prominent investors and cultural figures. But already, crypto security firms are pointing to suspicious transactions patterns, and warning the site has all the appearances of a scam.&lt;/p&gt;
&lt;h2&gt;Software &amp;amp; AI&lt;/h2&gt;
&lt;p&gt;Google is leading the charge to replace third-party cookies with a new suite of technologies to target ads on the Web. &lt;strong&gt;Federated Learning of Cohorts&lt;/strong&gt; (FLoC) is a new way that browsers could enable interest-based advertising on the web, in which the companies who today observe the browsing behavior of individuals instead observe the behavior of a cohort of similar people. The technology will avoid the privacy risks of third-party cookies, but it will create new ones in the process. It may also exacerbate many of the worst non-privacy problems with behavioral ads, including discrimination and predatory targeting.&lt;/p&gt;
&lt;p&gt;Ward describes debt as the natural result of writing code about something we don’t have a proper understanding of. But how do we land to such disagreement? In the author’s experience, there are two offenders:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;🎨 &lt;strong&gt;Wrong Design&lt;/strong&gt; — what we built was wrong from the start!&lt;/li&gt;
&lt;li&gt;🏃 &lt;strong&gt;Rapid Evolution&lt;/strong&gt; — we built the right thing, but the landscape changed quickly and made it obsolete.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Learn the history and evolution of same-origin policy and CORS, understand CORS and the different types of cross-origin access in depth, and learn (some) best practices.&lt;/p&gt;
&lt;p&gt;An estate agent has apologised after a 3D tour of a house for sale in Devon was published with a substantial amount of personal information visible. Financial paperwork in the study could be read by zooming in on the image. It included a shares dividend cheque, an insurance policy document and an invoice for a stairlift. Some family photos had also been left unblurred.&lt;/p&gt;
&lt;h2&gt;Culture &amp;amp; Fun&lt;/h2&gt;
&lt;p&gt;Easy 5 star book for me. The Silent Patient is a shocking psychological thriller of a woman’s act of violence against her husband—and of the therapist obsessed with uncovering her motive.&lt;/p&gt;
&lt;p&gt;Starting with an old cell phone a friend gave him, 17-year-old Steven Ortiz of Glendora, CA, used Craigslist to &lt;a href=&quot;http://www.whittierdailynews.com/ci_15535695&quot;&gt;trade up 14 times over two years&lt;/a&gt; and eventually ended up with a Porsche Boxster. Here’s how he did it.&lt;/p&gt;
&lt;p&gt;Known simply as “her,” she would become the star of My Octopus Teacher, the Oscar-nominated Netflix documentary and surprise pandemic hit that told the story of Foster’s unlikely relationship with that eight-armed mollusk.&lt;/p&gt;
&lt;p&gt;Video walk version of &lt;a href=&quot;https://www.geoguessr.com/&quot;&gt;GeoGuessr&lt;/a&gt;. Watch a video walk in a city and guess where it is on a map!&lt;/p&gt;
&lt;p&gt;The Australian government had to assert that COVID-19 vaccines do not – and cannot – connect you to the internet.&lt;/p&gt;
</content:encoded></item><item><title>Weekly Digest 14 - Neuralink MindPong, Muon g-2, and Foot Fetish</title><link>https://www.augusteo.com/blog/weekly-digest-14/</link><guid isPermaLink="true">https://www.augusteo.com/blog/weekly-digest-14/</guid><description>Weekly Digest 14 - Neuralink MindPong, Muon g-2, and Foot Fetish - a blog post by Victor Augusteo</description><pubDate>Sun, 11 Apr 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/weekly-digest-14/weekly-digest-14-202104damien-schnorhk-gigqsr1bnlw-unsplash-1.jpg&quot; alt=&quot;weekly-digest-14-202104damien-schnorhk-gigqsr1bnlw-unsplash-1.jpg&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Must Read&lt;/h3&gt;
&lt;p&gt;Neuralink is developing a fully-implanted, wireless, high-channel count brain-machine interface (BMI) with the goal of enabling people with paralysis to directly use their neural activity to operate computers and mobile devices with speed and ease. You can watch the video on how they enabled a macaque monkey, named Pager, to move a cursor on a computer screen with neural activity using a 1,024 electrode &lt;em&gt;fully-implanted neural recording and data transmission device&lt;/em&gt;, termed the N1 Link.&lt;/p&gt;
&lt;h3&gt;Science &amp;amp; Tech&lt;/h3&gt;
&lt;p&gt;Jorge Cham, aka, PHD Comics, illustrates the excitement over the muon anomaly results in a set of cartoons he made for Physics.&lt;/p&gt;
&lt;p&gt;Having a responsive, supportive partner minimizes the negative impacts of an individual’s depression or external stress on their romantic relationship, according to research by a University of Massachusetts Amherst social psychologist. The study found that being a responsive partner – one who focuses effort and energy to listen to their partner without reacting, tries to understand what’s being expressed and be supportive in a helpful way, and knows what their particular partner needs – is in general associated with better relationship quality, “which is what you would think,” Pietromonaco says.&lt;/p&gt;
&lt;p&gt;Feasts your eyes on these amazing photographs of ants.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;A review of&lt;/em&gt; &lt;a href=&quot;https://www.amazon.co.uk/Drink-Science-Alcohol-Your-Health-ebook/dp/B07SZLPS5B/&quot;&gt;&lt;em&gt;Drink?: The New Science of Alcohol and Your Health&lt;/em&gt;&lt;/a&gt; &lt;em&gt;by Professor David Nutt.&lt;/em&gt; Alcohol use is one of the top five causes of disease and disability in almost all countries in Europe. At least one in five cases of dementia is caused by alcohol. It is also the third leading risk factor for death and disability after smoking and obesity. The positive effect on cardiovascular health has never been definitely proven.&lt;/p&gt;
&lt;p&gt;Some drugs classified as highly dangerous are less harmful than alcohol or tobacco. Why is alcohol legal but many other intoxicants not?&lt;/p&gt;
&lt;p&gt;Coronavirus-related conspiracy theories (CT) have been found to be associated with fewer pandemic containment-focused behaviors. It is therefore important to evaluate associated cognitive factors. People who believe in COVID-19 conspiracy theories have the following cognitive biases: jumping-to-conclusions bias, bias against disconfirmatory evidence, and paranoid ideation, finds a new German study (n=1,684).&lt;/p&gt;
&lt;h3&gt;Business &amp;amp; Finance&lt;/h3&gt;
&lt;p&gt;Story of Missive, an innovative but somewhat hard-to-define email client. They reached &lt;strong&gt;US$1M&lt;/strong&gt; in annual recurring revenue (ARR) last week.&lt;/p&gt;
&lt;p&gt;Nearly 1.5m companies are incorporated in Delaware. How did this tiny state become a mecca for corporate activity? The answer is, of course, tax loopholes.&lt;/p&gt;
&lt;p&gt;“In sharp contrast to regulators’ concerns that WSB investment advice is harming retail traders, our findings suggest that both WSB posters and users are skilled,” the study concluded.&lt;/p&gt;
&lt;p&gt;PMC is what patent lawyers call a “non-practicing entity,” or NPE. The company has acquired over 100 patents that date back to applications from the 1980s, and it uses them to demand money from companies that do stuff—mostly internet companies.&lt;/p&gt;
&lt;p&gt;Three factors weigh heavily in any unionization election: the outrageously vicious behavior of employers—some of it illegal, most fully legal—including harassing and intimidating workers, and telling bold lies (which, outside of countries with openly repressive governments, is unique to the United States); the strategies and tactics used in the campaign by the organizers; and the broader social-political context in which the union election is being held.&lt;/p&gt;
&lt;p&gt;U.S. income inequality has varied inversely with union density over the past hundred years. The research finds consistent evidence that unions reduce inequality, explaining a significant share of the dramatic fall in inequality between the mid-1930s and late 1940s.&lt;/p&gt;
&lt;h3&gt;Software &amp;amp; AI&lt;/h3&gt;
&lt;p&gt;The Supreme Court surprised everyone with its API copyright ruling. By a 6-2 vote, the nation’s highest court held that Google’s copying of Oracle’s Java API was fair use. The ruling means Google won’t have to pay billions of dollars in damages to Oracle. It also has huge implications for the broader software industry. “It’s a minor miracle we got an opinion that reached the right result with pretty sound reasoning despite not one of the justices being able to explain how an API works,” Grimmelman said.&lt;/p&gt;
&lt;p&gt;Git &lt;a href=&quot;https://m.xkcd.com/1597/&quot;&gt;has a reputation&lt;/a&gt; for &lt;a href=&quot;https://stevelosh.com/blog/2013/04/git-koans/&quot;&gt;being confusing&lt;/a&gt;. Users stumble over terminology and phrasing that misguides their expectations. The author believes that Git becomes understandable if we peel back the curtain and look at how Git stores your repository data. After we investigate this model, we’ll explore how this new perspective helps us understand commands like git cherry-pick and git rebase.&lt;/p&gt;
&lt;p&gt;Experts are raising doubts that artificial intelligence was used to create a video that police are calling a “deepfake” and is at the center of an ongoing legal battle playing out in Pennsylvania. Several experts have come forth and said that the vaping video did not appear to be artificially-generated.&lt;/p&gt;
&lt;p&gt;The author breaks down the setup he uses to run a SaaS. From load balancing to cron job monitoring to payments and subscriptions. The business is &lt;a href=&quot;https://panelbear.com/&quot;&gt;https://panelbear.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Culture &amp;amp; Fun&lt;/h3&gt;
&lt;p&gt;“We’re receiving complaints about too much TV coverage of the death of HRH Prince Philip, Duke of Edinburgh,” reads a statement on the &lt;a href=&quot;https://www.bbc.co.uk/contact/complaints&quot;&gt;BBC Complaints page&lt;/a&gt;, which invites disgruntled viewers to submit an email address to register a complaint.&lt;/p&gt;
&lt;p&gt;A surprisingly revealing interview with a foot fetishist. e.g.&lt;strong&gt;What makes a foot attractive to you?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I like the painted toes. I like an arch, the more pronounced the better. I’m kinda weird with the toes, I like a rounded big toe. If it’s more square it’s okay, but the rounded is better. I definitely like the soles. But I like the arches, that gets you turned on.&lt;/p&gt;
</content:encoded></item><item><title>Weekly Digest 13 - NFTs, Facebook Hack, Microdosing, Inside istheshipstillstuck.com and others</title><link>https://www.augusteo.com/blog/weekly-digest-13/</link><guid isPermaLink="true">https://www.augusteo.com/blog/weekly-digest-13/</guid><description>Weekly Digest 13 - NFTs, Facebook Hack, Microdosing, Inside istheshipstillstuck.com and others - a blog post by Victor Augusteo</description><pubDate>Sun, 04 Apr 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/weekly-digest-13/weekly-digest-13-202104bhupesh-pal-l4gci05xl6y-unsplash-1.jpg&quot; alt=&quot;weekly-digest-13-202104bhupesh-pal-l4gci05xl6y-unsplash-1.jpg&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Must Read&lt;/h2&gt;
&lt;p&gt;NFTs are cryptocurrency tokens that represent the ownership in a digital asset. Like all digital assets, NFTs can be sent across the world immediately and require little to no physical storage. But unlike other digital assets, they are unique, cannot be duplicated, and can only be transferred by their owners. That’s why they’re used to represent ownership of digital assets. It got popularized recently because &lt;a href=&quot;https://www.washingtonpost.com/technology/2021/03/17/nft-beeple-metakovan-christies/&quot;&gt;someone paid $69 million&lt;/a&gt; for an NFT, and everyone else is scratching their heads wondering if these people have gone insane, or if it’s something they should pay attention to.&lt;/p&gt;
&lt;h2&gt;Science &amp;amp; Tech&lt;/h2&gt;
&lt;p&gt;Fascinating insight into how objects synchronize and how this affects everything from metronome, bridge building, to the celestial bodies like our moon.&lt;/p&gt;
&lt;p&gt;Great visualization to see how much of an anomaly year 2020 was. Lots of excess death on a stacking line graph.&lt;/p&gt;
&lt;p&gt;The personal data of over 500 million Facebook users has been posted online. The data includes phone numbers, full names, location, email address, and biographical information.&lt;/p&gt;
&lt;p&gt;Female bots are perceived to have more positive human qualities, such as warmth, experience and emotion, than male bots, and this greater humanness leads consumers to prefer female artificial intelligence, a new study has found.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Of the 93% of children who play video games, up to 40% opened loot boxes&lt;/li&gt;
&lt;li&gt;About 5% of gamers generate half the entire revenue from the boxes&lt;/li&gt;
&lt;li&gt;Twelve out of 13 studies on the topic have established “unambiguous” connections to problem gambling behaviour&lt;/li&gt;
&lt;li&gt;Young men are the most likely to use loot boxes - with young age and lower education correlating with increased uses&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Microdosing involves taking a low dose of a psychedelic drug to enhance performance, or reduce stress and anxiety. The 24 participants experienced personality changes after a period of microdosing. They found a decrease in neuroticism and an increase in conscientiousness (people who are highly conscientious tend to be diligent, for example). Interestingly, a greater amount of experience with microdosing was associated with lower levels of neuroticism among the 76 participants.&lt;/p&gt;
&lt;p&gt;The author of the article found that the book consistently overstates the problem of lack of sleep, sometimes egregiously so. It misrepresents basic sleep research and contradicts its own sources.&lt;/p&gt;
&lt;p&gt;It’s pretty much impossible to stop companies from collecting data about you — even if you don’t use their services, they still have their ways. But you can at least limit how algorithms use it against you. There are also more private browsers to limit data collection and prevent sites from linking you to your past visits or data. Or you can just stop using those services entirely.&lt;/p&gt;
&lt;p&gt;It seemed that patients were diagnosed with lung cancer at a surprisingly higher rate at 65 years old than, say, at 64 or 66. The researchers found a substantial rise nationwide in new cancer diagnoses at 65 — not only for lung cancer but also for breast, colon and prostate cancer. The four are the most common cancers in the United States.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In 2020, 47% of U.S. adults belonged to a church, synagogue or mosque&lt;/li&gt;
&lt;li&gt;Down more than 20 points from turn of the century&lt;/li&gt;
&lt;li&gt;Change primarily due to rise in Americans with no religious preference&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Business &amp;amp; Finance&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.socialinsider.io/blog/instagram-engagement/&quot;&gt;&lt;strong&gt;[Instagram Study] 102,700,573 Posts Show How Instagram Engagement Rate Decreases Back to Its Initial Values From 2019&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Higher Instagram engagement is mainly brought by carousels, especially for small accounts. Shorter captions, up to 10 words, combined with carousels form the posts to which users prefer to interact with. Carousels are getting more likes than any other media type. People are more open to comment on video posts across all profile sizes.&lt;/p&gt;
&lt;p&gt;Roth IRA is the best way to take advantage of current low income tax rates and set yourself up for years of tax-free income in retirement. However, people with high incomes are not allowed to contribute to a &lt;a href=&quot;https://www.fool.com/retirement/plans/roth-ira/&quot;&gt;Roth IRA&lt;/a&gt; directly. For them the only way into a Roth is through the back door – with a strategy appropriately known as the backdoor Roth.&lt;/p&gt;
&lt;p&gt;The COVID-19 pandemic accelerated digital adoption, especially in grocery shopping and healthcare, and this is expected to continue. Second, the pandemic and lockdowns reversed the long-standing trend of declining money and time spent at home, leading to “home nesting.” We expect this behavior to stick as some portion of high-income households will prefer to work from home to some degree after the pandemic and low-income households retain low-cost at-home alternatives such as digital entertainment.&lt;/p&gt;
&lt;p&gt;Most people who are rich chose their parents wisely. Bill Gates might not have ever figured out 1960s-style computer science but he had the foresight to pick a father who is one of the richest, most prominent lawyers in the state of Washington. And before he and Paul Allen made the deal with IBM that gave them a monopoly on the PC operating system, Bill had the foresight to choose a mother who was personally acquainted with John Opel, CEO of IBM Corporation. None of this would have worked if Bill hadn’t been willing to take tremendous personal risks. Should Microsoft have failed, of course, Bill Gates would have had nothing to fall back on but a million dollar trust fund from his mother’s parents (bankers) and the resumption of his degree program at Harvard College.&lt;/p&gt;
&lt;p&gt;This is an account of running &lt;a href=&quot;http://istheshipstillstuck.com/&quot;&gt;istheshipstillstuck.com&lt;/a&gt;. The website received around 50 million views in a 5 day period. That puts it roughly on par with the New York Times. The author tried many fun ways to monetize the traffic but didn’t have much success.&lt;/p&gt;
&lt;p&gt;A headline isn’t just an article title. It’s a tiny window of opportunity to connect with your audience. Top 5 insights:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Instructional headlines drove the most Facebook engagement.&lt;/li&gt;
&lt;li&gt;Curiosity headline phrases – ie. those that hinted, teased or questioned something – tended to fare a lot better on Twitter.&lt;/li&gt;
&lt;li&gt;The top headline phrase on Facebook had 590x more average shares than the top headline phrase on Twitter.&lt;/li&gt;
&lt;li&gt;The ideal headline length is 11 words and 65 characters, according to the most shared headlines on both Facebook &amp;amp; Twitter.&lt;/li&gt;
&lt;li&gt;The top Facebook headlines are no longer published by low-quality entertainment publications, but instead by authoritative news sites.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Software &amp;amp; AI&lt;/h2&gt;
&lt;p&gt;The basic functionality of fzf is very simple: it reads a set of lines from stdin, provides a user interface to select one or more lines, and writes the selected lines to stdout. This sounds very basic, but it is in fact very powerful.&lt;/p&gt;
&lt;p&gt;Columbia’s Aho and Stanford’s Ullman Developed Tools and Fundamental Textbooks Used by Millions of Software Programmers around the World. Two of their most widely celebrated books include: &lt;em&gt;&lt;strong&gt;The Design and Analysis of Computer Algorithms&lt;/strong&gt;&lt;/em&gt; (1974) and &lt;em&gt;&lt;strong&gt;Principles of Compiler Design&lt;/strong&gt;&lt;/em&gt; (1977)&lt;/p&gt;
&lt;h2&gt;Culture &amp;amp; Fun&lt;/h2&gt;
&lt;p&gt;I have never seen this method of learning foreign language before. Very novel!&lt;/p&gt;
&lt;p&gt;Marc Feren Claude Biart, an alleged member of the ‘Ndrangheta criminal organization based in southern Italy, reportedly hid his face in the cooking videos but failed to hide his tattoos, leading to his identification.&lt;/p&gt;
</content:encoded></item><item><title>Weekly Digest 12</title><link>https://www.augusteo.com/blog/weekly-digest-12/</link><guid isPermaLink="true">https://www.augusteo.com/blog/weekly-digest-12/</guid><description>Weekly Digest 12 - a blog post by Victor Augusteo</description><pubDate>Sun, 28 Mar 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/weekly-digest-12/weekly-digest-12-202103maria-vojtovicova-spvjpdxemqa-unsplash-1.jpg&quot; alt=&quot;weekly-digest-12-202103maria-vojtovicova-spvjpdxemqa-unsplash-1.jpg&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;World News&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.npr.org/2021/03/25/981128288/it-might-take-weeks-to-free-ship-stuck-in-suez-canal-salvage-company-says&quot;&gt;It ‘Might Take Weeks’ To Free Ship Stuck In Suez Canal&lt;/a&gt;. Meanwhile, shipping traffic is quickly backing up at both ends of the canal, which normally sees about 50 cargo ships transit each day. Several dozen vessels, including other large container ships, oil and gas tankers and bulk carriers, are creating what is being described as one of the worst maritime traffic jams in years. &lt;a href=&quot;https://www.reddit.com/r/Wellthatsucks/comments/mebj4d/after_evergreen_ship_blocking_suez_canal_now/&quot;&gt;Meanwhile, an Evergreen truck is blocking the highway in Taiwan too&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Old story from 2013.&lt;/p&gt;
&lt;p&gt;“The Egyptian government endeavors to require as many people as possible to be involved in a single ship’s passage through the Suez Canal. Over the course of ten hours and a mere 200 kilometers, we had seven different pilots come on board the CC Rigoletto. They worked in pairs but it seemed that one pilot did all the work while the other was only present to argue, talk loudly over their walkie-talkie, take personal calls and generally create significant noise.”&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.psypost.org/2021/03/watching-anthony-fauci-on-fox-news-makes-people-more-willing-to-engage-in-pandemic-reducing-behaviors-study-finds-60157&quot;&gt;&lt;strong&gt;Watching Anthony Fauci on Fox News makes people more willing to engage in pandemic reducing behaviors, study finds&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In an initial survey of 415 individuals, the researchers found that those with warmer feelings towards scientists were more concerned about COVID-19 and engaged in greater preventative behaviors, such as social distancing. This was true even after controlling for cognitive ability, education, gender, income, and political ideology.&lt;/p&gt;
&lt;h2&gt;Science &amp;amp; Tech&lt;/h2&gt;
&lt;p&gt;China’s companies have been unsuccessful in their bid to match TSMC’s manufacturing prowess, but the US has also started to struggle: Intel is set to outsource some production of processors, its crown jewel, to the Taiwanese company. In Washington, the Pentagon has been quietly pressing for the US to invest more in advanced chipmaking so that its weapons are not dependent on foreign manufacturers.&lt;/p&gt;
&lt;p&gt;This elegant study provides new evidence that people who are fluent in more than one language have some protection against dementia. Brain scans showed that lifelong bilinguals have stronger connections between certain brain areas compared to those who only speak one language – this appears to allow their brains to cope better with damage before they start to show outward signs of dementia.&lt;/p&gt;
&lt;p&gt;Folding A4 paper to quarks and back again to all of the observable universe.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.reddit.com/r/dataisbeautiful/comments/mbf6wg/oc_despite_being_far_more_selective_women_still/&quot;&gt;&lt;strong&gt;Despite being far more selective, women still match more frequently than men on Tinder&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;“Women match with 36% of those they like while men match with less than 2%”&lt;/p&gt;
&lt;p&gt;Penises are shrinking and genitals becoming malformed because of pollution, an environmental scientist has warned in a new book detailing the challenges facing human reproduction. Her book, titled &lt;strong&gt;Count Down&lt;/strong&gt;, examines “how our modern world is threatening sperm counts, altering male and female reproductive development, and imperilling the future of the human race”.&lt;/p&gt;
&lt;p&gt;The researchers examined the correlation between trends for forest cover, plantations, population and disease around the globe using statistics from international institutions such as the World Health Organization, the World Bank, the Food and Agricultural Organization and the &lt;a href=&quot;http://www.gideononline.com/&quot;&gt;Gideon epidemic database&lt;/a&gt;. Over the period of study from 1990 to 2016, this covered 3,884 outbreaks of 116 zoonotic diseases that crossed the species barrier and 1,996 outbreaks of 69 vector-borne infectious diseases, mostly carried by mosquitoes, ticks or flies. The paper shows outbreaks increased over time, while plantations expanded rapidly and overall forest cover declined gradually. By itself, a correlation is not proof of causality because other factors may be involved, such as climate disruption.&lt;/p&gt;
&lt;p&gt;When compared with our closest primate relatives, we have incredibly large brains. The researchers went on to identify a gene that is crucial to the process. Known as Zeb2, it switches on later in human tissue, allowing the cells to divide more before they mature. Tests showed that delaying the effects of Zeb2 made gorilla brain tissue grow larger, while turning it on sooner in human brain organoids made them grow more like the ape ones.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nouvelles.umontreal.ca/en/article/2021/03/22/does-harsh-parenting-lead-to-smaller-brains/&quot;&gt;&lt;strong&gt;A study shows that harsh parenting practices in childhood have long-term repercussions for children’s brain development.&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Repeatedly getting angry, hitting, shaking or yelling at children is linked with smaller brain structures in adolescence, according to a new study published in Development and Psychopathology.&lt;/p&gt;
&lt;h2&gt;Software &amp;amp; AI&lt;/h2&gt;
&lt;p&gt;Little-known company Flock has expanded from surveilling individual neighborhoods into a network of smart cameras that spans the United States. The cameras, which are sold to law enforcement, homeowners associations, and businesses, can automatically record when a “non-resident” vehicle drives into a community, and alert police to cars on a hotlist. Communities have created “virtual gates” around their neighborhoods, with cameras capturing each vehicle driving in and out of the area. Through a program called TALON, this little-known company is allowing police officers to track cars—and by extension, specific people—outside of their own jurisdictions.&lt;/p&gt;
&lt;p&gt;I use Vercel to host my foundation website. “Vercel provides a good solid solution for Serverless Functions and makes the process of their creation seamless and hassle-free. Cloudflare Workers offer more functionality out-of-the-box (e.g. key-value data store, CRON) and look more mature and sophisticated.”&lt;/p&gt;
&lt;p&gt;Todd Weaver pwns an IoT lamp.&lt;/p&gt;
&lt;p&gt;77.2% of Google mobile search ends with zero click. Google is reaping the rewards but depriving websites of the valuable visits.&lt;/p&gt;
&lt;h2&gt;Business&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://livingafi.com/2021/03/17/the-2021-early-retirement-update/&quot;&gt;&lt;strong&gt;The 2021 Early-Retirement Update&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Six years update from someone who retired early at 38yo with a $1m asset.&lt;/p&gt;
&lt;p&gt;How some of the top independent writers are making more than $78,400/month on Substack.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.indiehackers.com/post/john-onolan-of-ghost-3-3m-year-talks-creators-decentralization-no-code-b8c30b9977&quot;&gt;&lt;strong&gt;John O’Nolan of Ghost ($3.3M/year) talks creators, decentralization, no-code&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The non-profit blogging platform Ghost is now doing over $3 million a year. That’s up from $1.5 million this time last year, and the company’s never received any funding. Moreover, that’s annual recurring revenues from the subscriptions!&lt;/p&gt;
&lt;h2&gt;Culture &amp;amp; Fun&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://theprint.in/india/education/no-life-no-hobbies-burnout-lost-childhood-the-price-students-pay-for-a-prized-iit-seat/625040/&quot;&gt;&lt;strong&gt;No life, no hobbies, burnout, lost childhood — the price students pay for a prized IIT seat&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Last year, 1.5 million students took the JEE to qualify for 13,000 seats in 23 IITs across the country – in other words, for each seat there were 115 aspirants. So intense is the pressure and so gruelling is the preparation required that students as young as 14 start the process, often missing out on the simple joys of adolescence. Most give up extra-curricular activities, relationships with friends and peers, and all forms of entertainment to achieve the goal. By the time they achieve their aim, if they do, many realise they have lost out on social skills, ability to communicate easily with others (an attribute now known as soft skills), and of course, some part of their youth.&lt;/p&gt;
&lt;p&gt;An underrated family movie from the geniuses at Studio Ghibli. A love story between a girl who loves reading books, and a boy who has previously checked out all of the library books she chooses.&lt;/p&gt;
&lt;p&gt;How UX designers describe children.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.nbcnews.com/news/world/salmon-chaos-taiwan-people-change-their-names-get-free-sushi-n1261506&quot;&gt;&lt;strong&gt;‘Salmon chaos’ in Taiwan as people change their names to get free sushi&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The temporary deal from “&lt;a href=&quot;https://www.nbcnews.com/news/world/it-looks-end-world-china-s-worst-sandstorm-decade-chokes-n1261071&quot;&gt;Taiwan Sushiro&lt;/a&gt;” offered free all-you-can-eat sushi to anyone with “gui yu” — the Chinese characters for salmon — in their name.&lt;/p&gt;
</content:encoded></item><item><title>Weekly Digest 11</title><link>https://www.augusteo.com/blog/weekly-digest-11/</link><guid isPermaLink="true">https://www.augusteo.com/blog/weekly-digest-11/</guid><description>Weekly Digest 11 - a blog post by Victor Augusteo</description><pubDate>Sun, 21 Mar 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/weekly-digest-11/weekly-digest-11-202103eberhard-grossgasteiger-hxjkqhexaak-unsplash.jpg&quot; alt=&quot;weekly-digest-11-202103eberhard-grossgasteiger-hxjkqhexaak-unsplash.jpg&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Science &amp;amp; Technology&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://augusteo.msnd4.com/tracking/lc/c498b4fe-cfcd-4dee-8a89-f2409c8c31c1/cb4ac016-f8e8-4bb1-bae7-a688c0edc2f1/e3f320f1-c453-4d87-a3f0-fca5968168d2/&quot;&gt;&lt;strong&gt;Study suggests that men and women actually prefer not to split household and childcare tasks equally&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A new study suggests that the unequal division of household and childcare tasks within partnerships may reflect men’s and women’s actual preferences. For example, the study found that women enjoyed childcare tasks more than men did and also reported a greater desire for responsibility for these tasks compared to men. Men enjoyed tasks to do with outdoor labor and home maintenance more than women did. Women preferred cleaning, food prep, family scheduling, and home decorating. Moreover, this pattern mirrored the way the participants wanted these tasks to be divided — women tended to prefer men to take care of home maintenance tasks and men preferred women to take care of home decorating tasks.&lt;/p&gt;
&lt;p&gt;This research is an expansion of &lt;a href=&quot;http://augusteo.msnd4.com/tracking/lc/c498b4fe-cfcd-4dee-8a89-f2409c8c31c1/62a3fe9b-91ac-44e2-8165-9e978fc52b0a/e3f320f1-c453-4d87-a3f0-fca5968168d2/&quot;&gt;an earlier study&lt;/a&gt; showing that a vitamin D deficiency (less than 20 ng/ml) may raise the risk of testing positive for COVID-19. In the current study, those results were further supported, finding that individuals with a vitamin D deficiency had a 7.2% chance of testing positive for the virus. &lt;a href=&quot;http://augusteo.msnd4.com/tracking/lc/c498b4fe-cfcd-4dee-8a89-f2409c8c31c1/cdafe156-97fb-430b-ba00-3545c8b3bc06/e3f320f1-c453-4d87-a3f0-fca5968168d2/&quot;&gt;A separate study&lt;/a&gt; recently found that over 80% of patients diagnosed with COVID-19 were vitamin D deficient.&lt;/p&gt;
&lt;p&gt;Generalists vs specialists is not a new debate. There are areas where specialists have an advantage. Specialists do well in fields where there is one clear path to success. The world often hands us what are called “wicked problems”. These are settings where the rules of the game are unclear or incomplete. The patterns do not repeat and feedback is often delayed or misleading. The world is increasingly beginning to sound close to that description.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://augusteo.msnd4.com/tracking/lc/c498b4fe-cfcd-4dee-8a89-f2409c8c31c1/8cfcc987-a644-4a17-8eaa-135fe2a2edb6/e3f320f1-c453-4d87-a3f0-fca5968168d2/&quot;&gt;&lt;strong&gt;Milky Way, 12 years, 1250 hours of exposures and 125 x 22 degrees of sky&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It took nearly &lt;strong&gt;twelve years&lt;/strong&gt; to collect enough data for this high resolution gigapixel class mosaic image of the Milky Way. Total exposure time used is around &lt;strong&gt;1250 hours&lt;/strong&gt; between &lt;strong&gt;2009 and 2021.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;More than fifty published studies have documented meditation-induced mental health problems, including mania, dissociation, and psychosis. In 2012, leading meditation researchers in the United Kingdom published a set of guidelines for meditation instructors, noting “risks for participants,” including depression, traumatic flashbacks, and increased suicidal ideation.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://augusteo.msnd4.com/tracking/lc/c498b4fe-cfcd-4dee-8a89-f2409c8c31c1/96ca2769-9cd5-42c5-bcd9-4c435b4b6684/e3f320f1-c453-4d87-a3f0-fca5968168d2/&quot;&gt;&lt;strong&gt;Atlantic Ocean circulation at weakest in a millennium, say scientists&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Decline in the system underpinning Gulf Stream could lead to more extreme weather in Europe and higher sea levels on the US east coast. The Atlantic Ocean circulation that underpins the Gulf Stream, the weather system that brings warm and mild weather to Europe, is at its weakest in more than a millennium, and climate breakdown is the probable cause, according to new data.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Software &amp;amp; AI&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;A gaping flaw in SMS lets hackers take over phone numbers in minutes by simply paying a company to reroute text messages. “Looking down at my phone, there was no sign it had been hacked. I still had reception; the phone said I was still connected to the T-Mobile network. Nothing was unusual there. But the hacker had swiftly, stealthily, and largely effortlessly redirected my text messages to themselves. And all for just $16.”&lt;/p&gt;
&lt;p&gt;Imagine turning a 10-megapixel photo into a 40-megapixel photo. The term “Super Resolution” refers to the process of improving the quality of a photo by boosting its apparent resolution. Enlarging a photo often produces blurry details, but Super Resolution has an ace up its sleeve — an advanced machine learning model trained on millions of photos. Backed by this vast training set, Super Resolution can intelligently enlarge photos while maintaining clean edges and preserving important details.&lt;/p&gt;
&lt;p&gt;The company’s AI algorithms gave it an insatiable habit for lies and hate speech. The Cambridge Analytica scandal would kick off Facebook’s largest publicity crisis ever. It compounded fears that the algorithms that determine what people see on the platform were amplifying fake news and hate speech, and that Russian hackers had weaponized them to try to sway the election in Trump’s favor. Everything the company does and chooses not to do flows from a single motivation: Zuckerberg’s relentless desire for growth.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://augusteo.msnd4.com/tracking/lc/c498b4fe-cfcd-4dee-8a89-f2409c8c31c1/05a11c34-3943-40e5-8f44-a2dc327239c2/e3f320f1-c453-4d87-a3f0-fca5968168d2/&quot;&gt;&lt;strong&gt;Young female Japanese biker is really 50-year-old man with luscious hair using FaceApp&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/azusagakuyuki&quot;&gt;https://twitter.com/azusagakuyuki&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Given the state of technological advancement these days, one can never really believe what they see online all the time. The man reportedly said that nobody wants to see an “uncle”, and so, he turned himself into a “beautiful woman” so that his photos would be popular.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://augusteo.msnd4.com/tracking/lc/c498b4fe-cfcd-4dee-8a89-f2409c8c31c1/117e8f8c-0949-412f-b464-a4cbb73312b0/e3f320f1-c453-4d87-a3f0-fca5968168d2/&quot;&gt;&lt;strong&gt;What I learned in two years of moving government forms online&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The author started at the City of Boston with a simple yet comically broad mandate: Get rid of paper forms and PDFs. Over the course of two years, they identified 425 forms and moved 122 of them online. Using a conservative estimate, moving those forms online saved Boston residents just under 10,000 hours. They also made government services much more accessible.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://augusteo.msnd4.com/tracking/lc/c498b4fe-cfcd-4dee-8a89-f2409c8c31c1/b27cb121-e3fa-403d-9664-9b47d494a337/e3f320f1-c453-4d87-a3f0-fca5968168d2/&quot;&gt;&lt;strong&gt;Amazon driver quits, saying the final straw was the company’s new AI-powered truck cameras that can sense when workers yawn or don’t use a seatbelt&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Amazon was equipping all delivery vehicles with AI camera systems called Driveri, manufactured by a company called Netradyne. The cameras are always on and scan drivers’ body language, the speed of the vehicle, and even drowsiness. The system then uses “automated verbal alerts” to tell drivers if a violation has been detected.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://augusteo.msnd4.com/tracking/lc/c498b4fe-cfcd-4dee-8a89-f2409c8c31c1/4d543a9d-b846-4e05-9651-eced05a804c3/e3f320f1-c453-4d87-a3f0-fca5968168d2/&quot;&gt;&lt;strong&gt;Are Xiaomi browsers spyware? Yes, they are…&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;They send all visited websites to an analytics server. Once when the page starts loading, and another time when it finishes. And the Xiaomi blog post explains why this code exists: “The URL is collected to identify web pages which load slowly; this gives us insight into how to best improve overall browsing performance.” Are you convinced by this explanation?&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Business &amp;amp; Finance&lt;/strong&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Bond markets offer ridiculously low yields.&lt;/li&gt;
&lt;li&gt;The economics of investing in bonds (and most financial assets) has become stupid.&lt;/li&gt;
&lt;li&gt;The world is a) substantially overweighted in bonds (and other financial assets, especially US bonds) at the same time that b) governments (especially the US) are producing enormous amounts more debt and bonds and other debt assets.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And many more reasons.&lt;/p&gt;
&lt;p&gt;It’s 2021—it would be shocking if the New York Times wasn’t A/B testing headlines. The findings are very interesting.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://augusteo.msnd4.com/tracking/lc/c498b4fe-cfcd-4dee-8a89-f2409c8c31c1/e193af25-c8c4-4b06-b33e-73793f7d2b94/e3f320f1-c453-4d87-a3f0-fca5968168d2/&quot;&gt;&lt;strong&gt;How to do one-on-ones as a manager&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you are a line manager, your most important responsibility is to ensure the people you line manage are happy, engaged and are working as effectively as they can. One of the best ways to achieve this is to have quality one-on-ones with your line reports.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Culture &amp;amp; Fun&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://augusteo.msnd4.com/tracking/lc/c498b4fe-cfcd-4dee-8a89-f2409c8c31c1/301ec8c4-1c1f-47f8-81c1-18d45996c45b/e3f320f1-c453-4d87-a3f0-fca5968168d2/&quot;&gt;&lt;strong&gt;Reddit investors adopt 3,500 gorillas in six days&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Around 3,500 gorillas have been adopted by Reddit’s &lt;a href=&quot;http://augusteo.msnd4.com/tracking/lc/c498b4fe-cfcd-4dee-8a89-f2409c8c31c1/aeee7bb2-a5a7-4520-ba3c-48d2b720e404/e3f320f1-c453-4d87-a3f0-fca5968168d2/&quot;&gt;WallStreetBets&lt;/a&gt; (WSB) community in six days. The Dian Fossey Gorilla Fund International, a charity for the protection of endangered mountain gorillas, has also received more than $350,000 (£252,000) in donations.&lt;/p&gt;
&lt;p&gt;Zoom Escaper is a tool to help you escape Zoom meetings and other videoconferencing scenarios. It allows you to self-sabotage your audio stream, making your presence unbearable to others.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://augusteo.msnd4.com/tracking/lc/c498b4fe-cfcd-4dee-8a89-f2409c8c31c1/460336bc-4258-458b-a0d8-200f48650215/e3f320f1-c453-4d87-a3f0-fca5968168d2/&quot;&gt;&lt;strong&gt;Player calculates the cost of obtaining his dream team in FUT 21 in response to EA saying you don’t need to spend money. 22,000 hours or £80,000 required&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And related &lt;a href=&quot;http://augusteo.msnd4.com/tracking/lc/c498b4fe-cfcd-4dee-8a89-f2409c8c31c1/4464dead-d952-4196-96d1-a29f38947120/e3f320f1-c453-4d87-a3f0-fca5968168d2/&quot;&gt;Reddit discussion&lt;/a&gt; with the usual jokes.&lt;/p&gt;
&lt;p&gt;I liked the Netflix show and loved the book even more.&lt;/p&gt;
&lt;p&gt;Eight year-old orphan Beth Harmon is quiet, sullen, and by all appearances unremarkable. That is until she plays her first game of chess. Her senses grow sharper, her thinking clearer, and for the first time in her life she feels herself fully in control. By the age of sixteen, she’s competing for the U.S. Open championship. But as she hones her skills on the professional circuit, the stakes get higher, her isolation grows more frightening, and the thought of escape becomes all the more tempting. Engaging and fast-paced, &lt;em&gt;The Queen’s Gambit&lt;/em&gt; speeds to a conclusion as elegant and satisfying as a mate in four.&lt;/p&gt;
&lt;p&gt;Once considered crazy by fellow villagers, Indonesian eco-warrior Sadiman has turned barren hills green after 24 years of effort. Some even thought he was a madman because he bartered saplings for the goats he reared. Sadiman also funds his work through a nursery of plants such as cloves and jackfruit that he can sell or barter.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://augusteo.msnd4.com/tracking/lc/c498b4fe-cfcd-4dee-8a89-f2409c8c31c1/5ca32fbb-57a9-445d-8612-01d547cee4d9/e3f320f1-c453-4d87-a3f0-fca5968168d2/&quot;&gt;&lt;strong&gt;Meet NASA’s Flight Director of Mars Perseverance, Who Came to U.S. with $300 and Cleaned Bathrooms&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Diana Trujillo immigrated to the United States from Colombia when she was 17. “My parents got divorced when I turned 12. After that happened, my mom had nothing. No money. We didn’t even have food. We’d boil an egg and we’d cut it in half, and that was our lunch that day,” she said. To put herself through community college, she secured a housekeeping job, and was able to transfer to the University of Florida, where she majored in aerospace engineering.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://augusteo.msnd4.com/tracking/lc/c498b4fe-cfcd-4dee-8a89-f2409c8c31c1/408e57ef-10d6-46ea-9684-70ecf042f866/e3f320f1-c453-4d87-a3f0-fca5968168d2/&quot;&gt;&lt;strong&gt;ELI5: Why does an orchestra have many people playing the same instruments? How does it add to the overall performance?&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ten people place the horsehairs of their bows atop their strings and pull. They are all playing the same note. Their violins are the same size–or are they? They are almost the same size, but not quite; the margin of error in manufacturing is small, but greater than zero. The violins are made of different wood from different trees, each with its own voice. Each performer is pulling their bow at a slightly different speed. Their fingers are all placed expertly on the exact spot they need to be placed on their fingerboards, but as the violin is a fretless instrument, each finger is placed very slightly differently to all the rest, offering a barely discernible difference in pitch.&lt;/p&gt;
</content:encoded></item><item><title>BEF Learning &amp; Progress Update 2</title><link>https://www.augusteo.com/blog/bef-update-2/</link><guid isPermaLink="true">https://www.augusteo.com/blog/bef-update-2/</guid><description>BEF Learning &amp; Progress Update 2 - a blog post by Victor Augusteo</description><pubDate>Sat, 20 Mar 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/bef-update-2/bef-update-2-202103sebastian-unrau-sp-p7uut0tw-unsplash.jpg&quot; alt=&quot;bef-update-2-202103sebastian-unrau-sp-p7uut0tw-unsplash.jpg&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Fund Distribution&lt;/h2&gt;
&lt;p&gt;We have encountered unexpected problems during fund distributions—the main reason being that we didn’t screen the kids before finding a sponsor for them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Failure Analysis&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The (faulty) process before:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;School gave us a list of student&lt;/li&gt;
&lt;li&gt;We reached out to potential donors&lt;/li&gt;
&lt;li&gt;Once we matched a donor with a child, we will reach out to the parent to screen them&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We found several issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A few families rejected our help by saying that they don’t need the help right now and that we should help other more needy kids instead.&lt;/li&gt;
&lt;li&gt;One child was adamant that they wanted to drop out of school because they wanted to help their family make money. It took us a couple of weeks to convince the child and their family to accept our help and continue their study.&lt;/li&gt;
&lt;li&gt;A few families are uncontactable. We tried contacting them via phone calls, WhatsApp (the default comms method in Indonesia) and sometimes contacting the kids directly without any success.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;New &amp;amp; Improved Process&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;From now on, we will switch up the steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;School gave us a list of student&lt;/li&gt;
&lt;li&gt;We reach out to the families to screen them and make sure that they are receptive and require help&lt;/li&gt;
&lt;li&gt;Only then will we reach out to potential donors to raise funds&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Fundraising&lt;/h2&gt;
&lt;p&gt;We have entirely stopped fundraising while we close the loops on the first wave of 8 students.&lt;/p&gt;
&lt;p&gt;Once we identify the next group of student, we will start the fundraising again.&lt;/p&gt;
&lt;h2&gt;Business Entity Registration&lt;/h2&gt;
&lt;p&gt;We have registered Borneo Education Foundation with the State of California.&lt;/p&gt;
&lt;p&gt;I’m working to figure out the next steps now on the path to 501c3.&lt;/p&gt;
&lt;h2&gt;Website&lt;/h2&gt;
&lt;p&gt;The development is coming along. We aren’t in a hurry to get this done. The website’s primary purpose is for high scalability, and we aren’t ready for high volume yet.&lt;/p&gt;
&lt;p&gt;I will write a more in-depth blog post about the website architecture later.&lt;/p&gt;
&lt;p&gt;For now, I’m enjoying learning NextJs, Typescript, Serverless architecture, integration with Contentful, etc.&lt;/p&gt;
&lt;p&gt;Lots of fun!&lt;/p&gt;
</content:encoded></item><item><title>Borneo Education Foundation - Progress Update 1 - Mid February 2021</title><link>https://www.augusteo.com/blog/bef-update-1/</link><guid isPermaLink="true">https://www.augusteo.com/blog/bef-update-1/</guid><description>Borneo Education Foundation - Progress Update 1 - Mid February 2021 - a blog post by Victor Augusteo</description><pubDate>Sat, 13 Feb 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/bef-update-1/bef-update-1-202102tim-marshall-catzhuz7z8g-unsplash.jpg&quot; alt=&quot;bef-update-1-202102tim-marshall-catzhuz7z8g-unsplash.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Since I published my &lt;a href=&quot;https://hugo.augusteo.com/blog/im-starting-a-charity-to-help-underprivileged-children-in-borneo-indonesia-to-get-quality-education.-heres-why-and-how-you-can-help./&quot;&gt;previous blog post&lt;/a&gt; two weeks ago, we have made significant progress. I am grateful for all the help and interest from all of you!&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;KPI&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Total children helped: &lt;strong&gt;8&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Updates&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. We had paid for the tuition fee for two children this semester&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The children can now continue their education with one less worry. They are both covered until finishing high-school (at least).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Generous donors had pledged to sponsor six more children&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The donations come from donors in Indonesia, Australia, and the USA. Thanks for all the pledges!&lt;/p&gt;
&lt;p&gt;Some pledged to sponsor for a semester, some for a year, and some until the children finished with high-school.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Handful of donors are happy to donate and let us decide where to spend it&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We are very grateful for your trust!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. We have registered borneoeducationfoundation.org domain name&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This name reflects our focus to help children in Borneo to get a good education.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. We now have a web developer on board to help make the website&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;My high-school classmate, &lt;a href=&quot;https://wend.web.id/&quot;&gt;&lt;strong&gt;Wendy&lt;/strong&gt;&lt;/a&gt;, had volunteered to develop the website.&lt;/p&gt;
&lt;p&gt;A very early alpha version is in the works. Wendy wrote it in PHP with CodeIgniter.&lt;/p&gt;
&lt;h2&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. Registering 501©(3) organization&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;My immigration lawyers have just confirmed that I can register a charitable organization despite my visa status. They just wanted to make sure that I won’t be compensated in any way. This won’t be a problem since all the money raised will go to the children.&lt;/p&gt;
&lt;p&gt;If you know people who know how to do this best, please contact me!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Expanding partnership with other schools in Borneo&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Right now, we are working with one school group only (Santu Petrus in Pontianak). We are planning to reach out to other schools to help more students.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Recruit more volunteers to help and raise more money!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Things are starting to heat up, and we will need more help. If you wanted to get involved, please &lt;a href=&quot;mailto:victor@augusteo.com&quot;&gt;contact me&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>I&apos;m starting a charity to help underprivileged children in Borneo, Indonesia to get quality education. Here&apos;s why and how you can help.</title><link>https://www.augusteo.com/blog/starting-charity/</link><guid isPermaLink="true">https://www.augusteo.com/blog/starting-charity/</guid><description>I&apos;m starting a charity to help underprivileged children in Borneo, Indonesia to get quality education. Here&apos;s why and how you can help. - a blog post by Victor Augusteo</description><pubDate>Sat, 30 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/starting-charity/starting-charity-202101larm-rmah-aeatunvneik-unsplash.jpg&quot; alt=&quot;starting-charity-202101larm-rmah-aeatunvneik-unsplash.jpg&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Why Borneo, Indonesia?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Because no one is doing it right now.&lt;/p&gt;
&lt;p&gt;Searching for “West Borneo charity” or “Borneo education charity” on Google would show results for Orangutan &amp;amp; environment charities. Sure, it is good to help the animals, but we should help the human kids too.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/starting-charity/starting-charity-202102screen-shot-2021-02-01-at-10-23-29-pm.png&quot; alt=&quot;starting-charity-202102screen-shot-2021-02-01-at-10-23-29-pm.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;There are many of other foundation that focuses on education in Indonesia. However, every single one that I’ve checked focuses on Java and Bali - the most populous and famous places. I haven’t been able to find a single one that helps kids in Borneo.&lt;/p&gt;
&lt;p&gt;Some example: &lt;a href=&quot;https://www.emmanuel.foundation/outreach-programme&quot;&gt;Emmanuel&lt;/a&gt; &amp;amp; &lt;a href=&quot;https://www.collibrifoundation.org/en/project/indonesia-education-better-future&quot;&gt;Collibri&lt;/a&gt; both focus on Java.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Why education?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;I was born and raised in West Borneo (Kalimantan Barat), Indonesia. I moved elsewhere after finishing high school to pursue higher education.&lt;/p&gt;
&lt;p&gt;I am fortunate to be able to do the things I do now. It was all thanks to the education that I received along the way. I’m very thankful for the opportunity that I had received so far.&lt;/p&gt;
&lt;p&gt;Many other kids are smarter than me but didn’t have the resources to continue studying due to various reasons. Economic being a significant barrier.&lt;/p&gt;
&lt;p&gt;If we can take away the most significant barrier, they will surprise us with how high they can achieve.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;What about free public schooling?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;The government provides free primary education, but the quality is abysmal.&lt;/p&gt;
&lt;p&gt;Based on a conversation with Yayasan Usaba in Ketapang, they said that the government-sponsored public school has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;36% of teachers who skip coming to class but still got paid.&lt;/li&gt;
&lt;li&gt;Many principals only came twice to the school in a semester: at the beginning and end of the school semester.&lt;/li&gt;
&lt;li&gt;50% of kids in second grade still couldn’t read or write.&lt;/li&gt;
&lt;li&gt;20% of kids in sixth grade yet couldn’t read or write.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a result, the kids will have to spend a long time playing catch up with their peers who got good quality education.&lt;/p&gt;
&lt;p&gt;This data is limited to Ketapang and surrounding places, but the theme is clear. The public school’s quality is just awful.&lt;/p&gt;
&lt;h2&gt;What’s the finding so far?&lt;/h2&gt;
&lt;p&gt;One of my friends is now working as a high-school teacher at my &lt;a href=&quot;https://www.smapetrus.net/&quot;&gt;alma mater&lt;/a&gt;. She helped to contact the principal and administrator and produced a list of at-risk students.&lt;/p&gt;
&lt;p&gt;Some of the situations include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Recently orphaned, living with 80yo grandfather.&lt;/li&gt;
&lt;li&gt;Father passed away, mom working as a household maid.&lt;/li&gt;
&lt;li&gt;Father works as a daily laborer.&lt;/li&gt;
&lt;li&gt;Mom and dad divorced. Now the kid lives with 70yo grandmother.&lt;/li&gt;
&lt;li&gt;Lots of stories about parents losing their income due to the COVID pandemic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those kids would lose their chance at getting a quality education without our help.&lt;/p&gt;
&lt;p&gt;This school charges around IDR 660k (USD 46) per month per pupil.&lt;/p&gt;
&lt;p&gt;I contacted my primary &amp;amp; middle school alma-mater, Yayasan Usaba.&lt;/p&gt;
&lt;p&gt;They manage 13 schools ranging from primary to middle school, all located outside the city center of Ketapang. They serve 1,144 students and charge IDR 25-75k (USD 2-5) per month per pupil.&lt;/p&gt;
&lt;p&gt;The typical students are children of local laborers who worked for palm oil plantations, miners, farmers, and fishers. Their livelihood had been decimated by deforestation, the COVID situation, and other climate-change related issues.&lt;/p&gt;
&lt;h3&gt;Other charitable organizations&lt;/h3&gt;
&lt;p&gt;I’ve known of three other organizations and in the process of establishing better communication and understanding.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Love Borneo&lt;/strong&gt; - They raise funds and goes to remote villages to provide books to needy kids.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://yayasanpalung.com/&quot;&gt;&lt;strong&gt;Yayasan Palung&lt;/strong&gt;&lt;/a&gt; - They provide a college scholarship for students who want to give back to the Orang Utan conservation effort.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aku Belajar&lt;/strong&gt; - Recruits volunteers to teach poor kids in remote villages. They raise money by selling used clothing.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;How can we help the kids?&lt;/h2&gt;
&lt;p&gt;I see two ways to help.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get donors to sponsor individual kids to make sure they received adequate education until finishing college. The donor would get a regular update of the kid’s progress.&lt;/li&gt;
&lt;li&gt;Raise a big pool of money and donate to local charitable foundations to help alleviate the situation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Or both.&lt;/p&gt;
&lt;p&gt;Getting started with sponsoring kids seems to be an excellent way to start. I have personally sponsored two kids for this semester and looking to support more.&lt;/p&gt;
&lt;p&gt;My friends in Pontianak are currently helping to set up bank accounts, looking for potential at-risk kids, and surveying their situation to confirm that they needed help. All voluntarily.&lt;/p&gt;
&lt;p&gt;Thanks, &lt;strong&gt;Carol&lt;/strong&gt; and &lt;strong&gt;Budy&lt;/strong&gt;!&lt;/p&gt;
&lt;h2&gt;How can YOU get involved?&lt;/h2&gt;
&lt;p&gt;There are two ways you can help.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Become a donor and send money to help the kids. 100% of your donation would help the kids directly.&lt;/li&gt;
&lt;li&gt;Provide sweat capital. If you are passionate about helping kids, you can help us raise donations, create legal entities and accounts, spread the word, manage the logistics, etc.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is my first try at starting a charity. I need all the help I can get!&lt;/p&gt;
&lt;p&gt;Please get in touch, and I’ll be delighted to talk more about this.&lt;/p&gt;
</content:encoded></item><item><title>Best Way to Prepare and Consume Soylent</title><link>https://www.augusteo.com/blog/prepare-soylent/</link><guid isPermaLink="true">https://www.augusteo.com/blog/prepare-soylent/</guid><description>Best Way to Prepare and Consume Soylent - a blog post by Victor Augusteo</description><pubDate>Mon, 18 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/prepare-soylent/prepare-soylent-2021011_082720_soylent_pdpwebsite_powderpouchoriginal_1468x1100_a01-pouchshot_1500x1500_crop_center.jpg&quot; alt=&quot;prepare-soylent-2021011_082720_soylent_pdpwebsite_powderpouchoriginal_1468x1100_a01-pouchshot_1500x1500_crop_center.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I’ve been having Soylent lunch for approximately five years. I drank it every working day at lunch break. It is quick, nutritious, filling, and require very little preparation and clean up. Perfect for a lazy person like myself.&lt;/p&gt;
&lt;p&gt;Think of these prep steps as the culmination of my five years of experience in consuming Soylent optimally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hardware needed&lt;/strong&gt;: Blender with 24oz bottle. I use the &lt;a href=&quot;https://smile.amazon.com/Ninja-Personal-Smoothies-24-Ounce-BL480D/dp/B01N7Y3H73/&quot;&gt;Ninja brand&lt;/a&gt; and love it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fill the bottle with a little bit of water&lt;/li&gt;
&lt;li&gt;Put two scoops of Soylent&lt;/li&gt;
&lt;li&gt;Add whatever supplements you desire&lt;/li&gt;
&lt;li&gt;Add chunky-cut fruits. I usually use Apples and Pears&lt;/li&gt;
&lt;li&gt;Add soy milk or dairy milk if you are lactose tolerant.&lt;/li&gt;
&lt;li&gt;Blend them&lt;/li&gt;
&lt;li&gt;Drink&lt;/li&gt;
&lt;li&gt;Clean up&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Approximate time required: ~5 minutes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Photos and details&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Fill the bottle with a little bit of water.&lt;/strong&gt; I usually run the tap for 1-2 seconds. This step is necessary so that the powders don’t stick to the container during blending.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/prepare-soylent/prepare-soylent-202101img_3011.jpeg&quot; alt=&quot;prepare-soylent-202101img_3011.jpeg&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Put two scoops of Soylent.&lt;/strong&gt;
I find that this is enough to sate me. Add more or less as necessary for you.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/prepare-soylent/prepare-soylent-202101img_3012.jpeg&quot; alt=&quot;prepare-soylent-202101img_3012.jpeg&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Add whatever supplements you desire.&lt;/strong&gt;
I added Creatine Monohydrate here. Feel free to add whatever supplement you take.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/prepare-soylent/prepare-soylent-202101img_3013.jpeg&quot; alt=&quot;prepare-soylent-202101img_3013.jpeg&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Add chunky-cut fruits. I usually use Apples and Pears.&lt;/strong&gt;
You should de-core the fruits, then quarter them and cut them a little smaller to help with the blending.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/prepare-soylent/prepare-soylent-202101img_3014.jpeg&quot; alt=&quot;prepare-soylent-202101img_3014.jpeg&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Add soy milk or dairy milk if you are lactose tolerant.&lt;/strong&gt;
Fill it up! Usually, I went way over the “MAX FILL” line, but it still works fine.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/prepare-soylent/prepare-soylent-202101img_3015.jpeg&quot; alt=&quot;prepare-soylent-202101img_3015.jpeg&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;**Blend them for 30 seconds.
**&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/prepare-soylent/prepare-soylent-202101img_3016.jpeg&quot; alt=&quot;prepare-soylent-202101img_3016.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/prepare-soylent/prepare-soylent-202101img_3017.jpeg&quot; alt=&quot;prepare-soylent-202101img_3017.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After drinking, you need to rinse them off with water to prevent sticking. Then just put the dirty container into the dishwasher or wash manually.&lt;/p&gt;
</content:encoded></item><item><title>Introducing My New Weekly Digest Newsletter</title><link>https://www.augusteo.com/blog/weekly-digest-intro/</link><guid isPermaLink="true">https://www.augusteo.com/blog/weekly-digest-intro/</guid><description>Introducing My New Weekly Digest Newsletter - a blog post by Victor Augusteo</description><pubDate>Sat, 02 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/weekly-digest-intro/weekly-digest-intro-202101rinck-content-studio-rmubashufwo-unsplash.jpg&quot; alt=&quot;weekly-digest-intro-202101rinck-content-studio-rmubashufwo-unsplash.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Do you want to get interesting links sent directly to your mailbox?&lt;/p&gt;
&lt;p&gt;I spent many hours every day reading news, articles, and books. That’s on top of hours of listening to music, watching movies and TV shows, and playing games.&lt;/p&gt;
&lt;p&gt;As a result, I’ve gathered a pile of contents that I loved that are too short for a blog post. So far, I’ve only shared those links with friends and family through chat or Facebook.&lt;/p&gt;
&lt;p&gt;Now I wanted to open up these curated links to you.&lt;/p&gt;
&lt;p&gt;You will receive an email every weekend with several links that I loved with a short description. The main goal is to provide you with a scan-able list so you can decide whether to open the link.&lt;/p&gt;
&lt;p&gt;Since this is an entirely new thing for me, I appreciate your feedback on the kind of content that you like or don’t like to see.&lt;/p&gt;
&lt;p&gt;To get started, please enter your email address below.&lt;/p&gt;
</content:encoded></item><item><title>Victor&apos;s 2020 Book Awards</title><link>https://www.augusteo.com/blog/2020-book-awards/</link><guid isPermaLink="true">https://www.augusteo.com/blog/2020-book-awards/</guid><description>Victor&apos;s 2020 Book Awards - a blog post by Victor Augusteo</description><pubDate>Sat, 19 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/2020-book-awards/2020-book-awards-202012giorgio-trovato-_xty6ld8jgm-unsplash.jpg&quot; alt=&quot;2020-book-awards-202012giorgio-trovato-_xty6ld8jgm-unsplash.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;These are the best &lt;a href=&quot;https://www.goodreads.com/user/year_in_books/2020/4577855&quot;&gt;books I read this year&lt;/a&gt;. In no particular order.&lt;/p&gt;
&lt;h3&gt;“We will need an extended version” award&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/5048174-2br02b&quot;&gt;&lt;strong&gt;2BR02B&lt;/strong&gt;&lt;/a&gt; by Kurt Vonnegut&lt;/p&gt;
&lt;p&gt;I love this short book. The world-building is excellent, and the promise of immortality would make you think about the societal implications. I would love to read the full-length novel.&lt;/p&gt;
&lt;h3&gt;“Even better after second reading” award&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/series/49075-the-stormlight-archive&quot;&gt;&lt;strong&gt;Stormlight Archive&lt;/strong&gt;&lt;/a&gt; series by Brandon Sanderson&lt;/p&gt;
&lt;p&gt;I re-read the whole series a second time in preparation for the fourth book’s release. The story gripped me even more, and I noticed many more details that I missed the first time. Highly recommended.&lt;/p&gt;
&lt;h3&gt;“Oh, I didn’t know that!” award&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/40718726-genghis-khan-and-the-making-of-the-modern-world&quot;&gt;&lt;strong&gt;Genghis Khan and the Making of the Modern World&lt;/strong&gt;&lt;/a&gt; by Jack Weatherford&lt;/p&gt;
&lt;p&gt;We all knew that the Mongols were just bloodthirsty barbarians who loved conquest and massacre. But do you know their real motivation and contributions to the world? You might be surprised!&lt;/p&gt;
&lt;h3&gt;“Remember your mortality” award&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/25899336-when-breath-becomes-air&quot;&gt;&lt;strong&gt;When Breath Becomes Air&lt;/strong&gt;&lt;/a&gt; by Paul Kalanithi&lt;/p&gt;
&lt;p&gt;High achieving neuro-surgeon was still a mortal. Paul’s story will give you a candid and personal look into his journey to the end of his life. Prepare tissue.&lt;/p&gt;
&lt;h3&gt;“Required yearly re-reading” award&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/39863499-how-to-think-like-a-roman-emperor&quot;&gt;&lt;strong&gt;How to Think Like a Roman Emperor: The Stoic Philosophy of Marcus Aurelius&lt;/strong&gt;&lt;/a&gt; by Donald J Robertson&lt;/p&gt;
&lt;p&gt;Robertson did a great job distilling the life philosophy of the late emperor. You can’t control what happens to you, but you can manage your reaction to it. It sounded simple yet tough to do consistently. This book will show you how.&lt;/p&gt;
&lt;h3&gt;“Action-adventure poetry” award&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/11250317-the-song-of-achilles&quot;&gt;&lt;strong&gt;The Song of Achilles&lt;/strong&gt;&lt;/a&gt; by Madeline Miller&lt;/p&gt;
&lt;p&gt;You will fall in love with the characters and empathize with their pain and cheer them on their journeys. They can’t help but be tangled in the history-defining fateful encounters.&lt;/p&gt;
&lt;h3&gt;“Completely unexpected delight” award&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/32620332-the-seven-husbands-of-evelyn-hugo&quot;&gt;&lt;strong&gt;The Seven Husbands of Evelyn Hugo&lt;/strong&gt;&lt;/a&gt; by Taylor Jenkins Reid&lt;/p&gt;
&lt;p&gt;This book isn’t the usual type of book that I would even consider reading. I’m so glad that I read it after recommended by a friend. I mean, who doesn’t like a story about a woman with seven husbands!&lt;/p&gt;
&lt;h3&gt;“How far can you take your lies” award&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/37976541-bad-blood&quot;&gt;&lt;strong&gt;Bad Blood: Secrets and Lies in a Silicon Valley Startup&lt;/strong&gt;&lt;/a&gt; by John Carreyrou&lt;/p&gt;
&lt;p&gt;The saga of Theranos’s rise and fall has been popularized in the media lately. This book will give you the most profound insight with interviews and stories from many people who are personally involved with the company.&lt;/p&gt;
&lt;h3&gt;“Just fascinating” award&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/37638098-creative-selection&quot;&gt;&lt;strong&gt;Creative Selection: Inside Apple’s Design Process During the Golden Age of Steve Jobs&lt;/strong&gt;&lt;/a&gt; by Ken Kocienda&lt;/p&gt;
&lt;p&gt;I’ve been working at Apple for three years now, but it seems like I knew very little about how things worked. It’s fascinating to read about some people with who I interacted in real life.&lt;/p&gt;
&lt;h3&gt;“Doesn’t have to be a book to move you” honorary award&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://nickbostrom.com/fable/dragon.html&quot;&gt;&lt;strong&gt;The Fable of the Dragon-Tyrant&lt;/strong&gt;&lt;/a&gt; by Nick Bostrom&lt;/p&gt;
&lt;p&gt;Defeating Death is not just a pipe dream. This short story rings even more real this year with the Covid-19 pandemic. Humanity has spent way too much time ignoring our existential crises. We should put more serious effort into making real progress towards life extension.&lt;/p&gt;
</content:encoded></item><item><title>Nightstand - Woodworking Project Step by Step</title><link>https://www.augusteo.com/blog/nightstand-woodworking-project/</link><guid isPermaLink="true">https://www.augusteo.com/blog/nightstand-woodworking-project/</guid><description>Nightstand - Woodworking Project Step by Step - a blog post by Victor Augusteo</description><pubDate>Thu, 10 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The final product sitting beside by bed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-00.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-00.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;First we start with the cutlist and Sketchup design.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-01.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-01.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Cut the boards to size with table saw and circular saw.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-02.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-02.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-03.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-03.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now it’s time to glue the panel to the Redwood beam&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-04.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-04.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-05.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-05.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now we can use pocket screws to make the joint stronger. I admit that I probably went way overboard with the pocket screws. I ended up only using half of those holes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-06.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-06.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Looking like a table now!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-07.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-07.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now we can move on to assemble the drawers with glue and pocket screws.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-08.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-08.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-09.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-09.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-10.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-10.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Drawers looking good. Don’t worry about the holes since we will use a facade in front of these that will cover up the holes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-11.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-11.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Time to put on the railing.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-12.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-12.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Once the railing is done, we can put on the drawers. I used Kreg’s drawer jig to make this easier.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-14.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-14.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Putting up the facade with glue and pin nails. You can’t have enough clamps.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-15.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-15.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Looking great!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-16.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-16.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now it’s time to paint and stain them. I use Behr white paint on the surfaces and drawers.
The Redwood beam and side panels are stained with Varathane Dark Walnut stain and finished with water based Polyurethane.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-17.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-17.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Time to install the drawer handles. I used some newspaper to correctly position the holes for drilling. You just need to put the drawer handle on a newspaper and poke the hole where the screws should be.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-18.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-18.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Clamping a wood to prevent blowout during drilling.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-19.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-19.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Handles installed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-20.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-20.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-21.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-21.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Sitting proudly beside by bed! Mission accomplished!&lt;/p&gt;
&lt;p&gt;The pocket holes are still quite visible as you can see. I’ve decided to not do anything with them since in most circumstances, we won’t ever get to see them much.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nightstand-woodworking-project/nightstand-woodworking-project-202012nightstand-22.jpeg&quot; alt=&quot;nightstand-woodworking-project-202012nightstand-22.jpeg&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Think Life a Freak Book Club Discussion Points</title><link>https://www.augusteo.com/blog/think-like-a-freak/</link><guid isPermaLink="true">https://www.augusteo.com/blog/think-like-a-freak/</guid><description>Think Life a Freak Book Club Discussion Points - a blog post by Victor Augusteo</description><pubDate>Fri, 04 Sep 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/think-like-a-freak/think-like-a-freak-202009think-like-a-freak-1.jpg&quot; alt=&quot;think-like-a-freak-202009think-like-a-freak-1.jpg&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;how would you describe what it means to “think Like a Freak”? Do you think like a Freak? Would you encourage others to think like a Freak?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Why do Levitt and Dubner want you to “put away your moral compass”? How do you think your best friend would react if you encouraged her to put away her moral compass? How would you respond?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Describe the broad lessons that you learned from Kobi’s experience as a world famous hotdog-eating champion. How would you apply these lessons to a situation that you are currently facing&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Problems are more likely to be resolved when causes, not symptoms, are addressed. Can you provide an example from your community that highlights a situation when symptoms are being targeted instead addressing root causes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The authors ask, “Why is it so important to have fun?” Thinking like a Freak, what would your answer to this question be?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Do you believe you can persuade someone who doesn’t want to be persuaded? Think about someone who holds a different opinion than you about politics, the environment, taxes, or any other issue. What approach would you use to try to change his or her viewpoint?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Effectively using incentives means understanding what they are. Can you give some examples of financial, social, moral, legal or other kinds of incentives and describe how they might work in one setting and backfire in another.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This book introduces the concepts of declared preferences and revealed preferences. Explain the difference between these and offer examples of these two types of preferences that you have observed in your own experience.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Why do the authors advise a would-be persuader to steer clear of anecdotes? Do you agree with them? What is the problem with anecdotes?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The authors describe themselves as happy, serial quitters. What ventures or activities have you quit early and do you regret it?&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Drafted and edited from &lt;a href=&quot;http://files.harpercollins.com/HarperAcademic/ThinkLikeaFreakTG.pdf&quot;&gt;various&lt;/a&gt; &lt;a href=&quot;https://b0f646cfbd7462424f7a-f9758a43fb7c33cc8adda0fd36101899.ssl.cf2.rackcdn.com/reading-guides/RG-9780062218339.pdf&quot;&gt;sources&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>When Breath Becomes Air by Paul Kalanithi</title><link>https://www.augusteo.com/blog/when-breath-becomes-air/</link><guid isPermaLink="true">https://www.augusteo.com/blog/when-breath-becomes-air/</guid><description>When Breath Becomes Air by Paul Kalanithi - a blog post by Victor Augusteo</description><pubDate>Mon, 27 Jul 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/when-breath-becomes-air/when-breath-becomes-air-202007wallpaperflare-1-1.jpg&quot; alt=&quot;when-breath-becomes-air-202007wallpaperflare-1-1.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;5 out of 5 stars.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Death comes for all of us. For us, for our patients: it is our fate as living, breathing, metabolizing organisms.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This book made me cry several times. The author was able to tell his incredible story while stricken with cancer and working long hours as a neurosurgeon. His prose is terrific. The book explored interesting medical details and contrasting it to the big pictures of what makes life worth living.&lt;/p&gt;
&lt;p&gt;It followed Paul’s story from his childhood to his death. Both his dad and his brother are doctors. He first studied English literature and transitioned to medical history before training as neurosurgeon and neuroscientist. His mom kindled his love for literature by pushing her kids to read a lot of books in the ‘college prep reading list.’&lt;/p&gt;
&lt;p&gt;One day during his training, he visited a home for people with brain injury. There he encountered the point where studies met reality. Some patients are just left alone by themselves without any visitors. One of the professors even said that it was better for the patients to die. That event helped Paul to contemplate mortality from the start. He noted that “Neurosurgery attracted me as much for its intertwining of brain and consciousness as for its intertwining of life and death.”&lt;/p&gt;
&lt;p&gt;You will read about medical details like how formaldehyde is a potent appetite stimulant. You’ll get hungry even while dissecting cadaver -or as they call it now, donors’ bodies. You’ll also read about how extremely overworked the doctors are with residents working upwards of one hundred hours per week.&lt;/p&gt;
&lt;p&gt;He was a high achieving young professional with a bright future. He has excellent jobs lined up and research that would help many people. But, alas, fate has something else in store.&lt;/p&gt;
&lt;p&gt;One day, his CT scan showed cancerous growth. His role instantly switched from doctor to patient. You’ll follow his harrowing story from the diagnosis to the first improvement and then to the eventual metastasis and death.&lt;/p&gt;
&lt;p&gt;I liked how his oncologist never discussed prognosis and focused on finding his values and living in the present. Not ignoring his condition, but not narrowing his experience to just cancer. Under her care, he temporarily returned to neurosurgery before having to tap out due to a weakening body.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Time for me is now double-edged: every day brings me further from the low of my last relapse but closer to the next recurrence—and, eventually, death. Perhaps later than I think, but certainly sooner than I desire.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The epilogue by his wife closed out the book beautifully. You’ll learn how his family responds to his cancer and how their bond helps one another during a difficult time.&lt;/p&gt;
&lt;p&gt;In summary, I highly recommend this book. It is one of the best memoirs I’ve read and one of the saddest.&lt;/p&gt;
&lt;p&gt;You will confront your mortality, success, and values and hopefully live your life just a bit better.&lt;/p&gt;
</content:encoded></item><item><title>If It Bleeds by Stephen King - Book Review</title><link>https://www.augusteo.com/blog/if-it-bleeds/</link><guid isPermaLink="true">https://www.augusteo.com/blog/if-it-bleeds/</guid><description>If It Bleeds by Stephen King - Book Review - a blog post by Victor Augusteo</description><pubDate>Sun, 19 Jul 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/if-it-bleeds/if-it-bleeds-91cd3nzganl-2.jpg&quot; alt=&quot;if-it-bleeds-91cd3nzganl-2.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;4 out of 5 stars overall. Recommended for anyone who loves a bit of mystery.&lt;/p&gt;
&lt;p&gt;The book is broken into four short stories of varying lengths.&lt;/p&gt;
&lt;p&gt;This is the first Stephen King book that I read. Definitely made me want to read his other works.&lt;/p&gt;
&lt;h3&gt;“Mr. Harrigan’s Phone”&lt;/h3&gt;
&lt;p&gt;5 out of 5 stars.&lt;/p&gt;
&lt;p&gt;This is my favorite story from the book. Short and sweet with strong and memorable characters.&lt;/p&gt;
&lt;p&gt;I especially love the focus on the iPhone and the wonder of being connected to the world in real-time.&lt;/p&gt;
&lt;h3&gt;“The Life of Chuck”&lt;/h3&gt;
&lt;p&gt;3 out of 5 stars.&lt;/p&gt;
&lt;p&gt;I felt like I was promised a grand journey, but cut short without any explanation to the many mysteries it served.&lt;/p&gt;
&lt;p&gt;Why was the world ending?&lt;/p&gt;
&lt;p&gt;Why did Chuck’s face got plastered everywhere?&lt;/p&gt;
&lt;p&gt;What triggered the mass suicides?&lt;/p&gt;
&lt;p&gt;Chuck seemed like just a regular dude who stumbled upon a magical attic. Nothing from his backstory even hints at any answers to the questions above.&lt;/p&gt;
&lt;p&gt;If Mr. King decided to write a full novel to expand this story, I’d read it in a heartbeat.&lt;/p&gt;
&lt;h3&gt;“If It Bleeds”&lt;/h3&gt;
&lt;p&gt;4 out of 5 stars.&lt;/p&gt;
&lt;p&gt;This is a clinic in crafting flawed yet believable characters. I empathized with the decision that the character made. The antagonist is fascinating but felt a bit too weak and easy to defeat. It was supposed to be a (near) immortal who had lived for hundreds of years, so I expected it to be stronger. Seems like it was just explained away with immortal being underestimating mere humans.&lt;/p&gt;
&lt;p&gt;Imagine that if you live more carefully, you can (probably) never die. You would definitely take extra steps to ensure that you survive and continue living.&lt;/p&gt;
&lt;p&gt;This antagonist just went into the trap without much preparation on weapons nor exit strategy. A bit ridiculous if you asked me.&lt;/p&gt;
&lt;h3&gt;“Rat”&lt;/h3&gt;
&lt;p&gt;3 out of 5 stars.&lt;/p&gt;
&lt;p&gt;The other stories felt like it happened in reality.&lt;/p&gt;
&lt;p&gt;This story felt like it is a hallucination of a schizophrenic person. Maybe that is what the author intended.&lt;/p&gt;
&lt;p&gt;The deal with the devil felt too easy too. Usually, you had to sacrifice something that you really love, either part of your body or your direct family. Somehow this devil is willing to take a friend’s life.&lt;/p&gt;
&lt;p&gt;Way too easy.&lt;/p&gt;
</content:encoded></item><item><title>How to set up Pi-Hole on AsusWRT Merlin from scratch for home-wide ad-blocking</title><link>https://www.augusteo.com/blog/pihole-asuswrt-merlin/</link><guid isPermaLink="true">https://www.augusteo.com/blog/pihole-asuswrt-merlin/</guid><description>How to set up Pi-Hole on AsusWRT Merlin from scratch for home-wide ad-blocking - a blog post by Victor Augusteo</description><pubDate>Mon, 01 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/pihole-asuswrt-merlin/pihole-asuswrt-merlin-202006pawel-czerwinski-ppo9tjzjcpg-unsplash-1.jpg&quot; alt=&quot;pihole-asuswrt-merlin-202006pawel-czerwinski-ppo9tjzjcpg-unsplash-1.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;There’s a world-wide pandemic raging, nation-wide riots over police brutality, lockdown, and curfew. What a perfect time to set up Pi-Hole.&lt;/p&gt;
&lt;p&gt;I used NextDNS and loved it, as evidenced by my &lt;a href=&quot;https://hugo.augusteo.com/blog/how-to-use-nextdns-with-asuswrt-merlin-tutorial/&quot;&gt;previous tutorial post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However, I just moved house recently and found my old Raspi 3 B in some old box. I am definitely guilty of starting some random tech project even though I should probably unpack the millions of boxes in the garage.&lt;/p&gt;
&lt;p&gt;This post will list the steps I took to set up the Pi-Hole.&lt;/p&gt;
&lt;h2&gt;1. Prepare your MicroSD card&lt;/h2&gt;
&lt;p&gt;Download the &lt;a href=&quot;https://www.raspberrypi.org/downloads/&quot;&gt;Raspi Imager&lt;/a&gt; for your OS.&lt;/p&gt;
&lt;p&gt;I chose the &lt;strong&gt;Lite&lt;/strong&gt; version since we won’t be needing any desktop environment. We are running headless! Just plug in your micro SD card and wait for it to finish.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/pihole-asuswrt-merlin/pihole-asuswrt-merlin-202006screen-shot-2020-06-01-at-11-41-20-pm.jpg&quot; alt=&quot;pihole-asuswrt-merlin-202006screen-shot-2020-06-01-at-11-41-20-pm.jpg&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;2. Config your Raspi&lt;/h2&gt;
&lt;p&gt;Once the Raspi is on, you can log in with &lt;code&gt;pi&lt;/code&gt; as username and &lt;code&gt;raspberry&lt;/code&gt; as a password.&lt;/p&gt;
&lt;p&gt;Then run &lt;code&gt;sudo raspi-config&lt;/code&gt; to change the default password, set up hostname (i call it &lt;code&gt;pi&lt;/code&gt;), enable ssh, and other localization options.&lt;/p&gt;
&lt;h2&gt;3. Run headless mode&lt;/h2&gt;
&lt;p&gt;Now that your Pi setup is done, feel free to unplug the keyboard and HDMI cables and let it run headless.&lt;/p&gt;
&lt;p&gt;Open up your main machine and run &lt;code&gt;ssh-copy-id pi@pi&lt;/code&gt; to enable password-less login next time.&lt;/p&gt;
&lt;p&gt;Now &lt;code&gt;ssh pi@pi&lt;/code&gt; should work flawlessly.&lt;/p&gt;
&lt;h2&gt;4. Install Pi-Hole&lt;/h2&gt;
&lt;p&gt;All you need to do is run this command:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl -sSL[https://install.pi-hole.net](https://install.pi-hole.net/)| bash&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I enabled the default 4 block list. It seems to work well.&lt;/p&gt;
&lt;p&gt;Once it’s all set up, you should be able to login to the dashboard with the supplied password. Of course, we don’t want to use the short password, so head over to terminal and type &lt;code&gt;pihole -a -p&lt;/code&gt; to change the password.&lt;/p&gt;
&lt;h2&gt;5. Configure DNS over HTTPS&lt;/h2&gt;
&lt;p&gt;You wouldn’t browse the web over HTTP, so why would you let people watch your DNS queries? No, no, no.&lt;/p&gt;
&lt;p&gt;The team wrote a charming &lt;a href=&quot;https://docs.pi-hole.net/guides/dns-over-https/&quot;&gt;guide here&lt;/a&gt;, so you should follow that.&lt;/p&gt;
&lt;p&gt;Just remember to use the ARM architecture.&lt;/p&gt;
&lt;p&gt;I used the automated way to start the daemon. What could go wrong?&lt;/p&gt;
&lt;h2&gt;6. Setup AsusWRT Merlin with Pi-Hole&lt;/h2&gt;
&lt;p&gt;This is the last step. Congratz.&lt;/p&gt;
&lt;p&gt;Some kind person has written an excellent step-by-step &lt;a href=&quot;https://www.reddit.com/r/pihole/comments/dfm5j4/guide_for_asuswrtmerlin_users_with_screenshots/&quot;&gt;over on Reddit&lt;/a&gt;. Here are the screenshots of my setup for your reference.&lt;/p&gt;
&lt;p&gt;LAN -&amp;gt; DHCP Server&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/pihole-asuswrt-merlin/pihole-asuswrt-merlin-202006screen-shot-2020-06-02-at-12-09-21-am.jpg&quot; alt=&quot;pihole-asuswrt-merlin-202006screen-shot-2020-06-02-at-12-09-21-am.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;LAN -&amp;gt; DNSFilter&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/pihole-asuswrt-merlin/pihole-asuswrt-merlin-202006screen-shot-2020-06-02-at-12-10-24-am.jpg&quot; alt=&quot;pihole-asuswrt-merlin-202006screen-shot-2020-06-02-at-12-10-24-am.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;WAN -&amp;gt; Internet Connection&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/pihole-asuswrt-merlin/pihole-asuswrt-merlin-202006screen-shot-2020-06-02-at-12-11-31-am.jpg&quot; alt=&quot;pihole-asuswrt-merlin-202006screen-shot-2020-06-02-at-12-11-31-am.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;And that’s it. Enjoy your ad-free home internet!&lt;/p&gt;
&lt;p&gt;After some time, you should be able to see a bunch of information on the admin panel.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/pihole-asuswrt-merlin/pihole-asuswrt-merlin-202006screen-shot-2020-06-02-at-12-15-24-am.jpg&quot; alt=&quot;pihole-asuswrt-merlin-202006screen-shot-2020-06-02-at-12-15-24-am.jpg&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Criticism and Book Club Discussion Points for Talking to Strangers by Malcolm Gladwell</title><link>https://www.augusteo.com/blog/talking-to-strangers-criticism/</link><guid isPermaLink="true">https://www.augusteo.com/blog/talking-to-strangers-criticism/</guid><description>Criticism and Book Club Discussion Points for Talking to Strangers by Malcolm Gladwell - a blog post by Victor Augusteo</description><pubDate>Sun, 23 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/talking-to-strangers-criticism/talking-to-strangers-criticism-202002talking-to-strangers.jpg&quot; alt=&quot;talking-to-strangers-criticism-202002talking-to-strangers.jpg&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Criticisms of the book&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;James Mitchell&lt;/strong&gt; who interrogated KSM:&lt;/p&gt;
&lt;p&gt;“Gladwell implies that Enhanced Interrogation Techniques (EITs) were (1) used to pressure KSM into “confessing” to attacks; and (2), that efforts to question KSM about future attacks were marred because KSM was being subjected to EITs when he provided information that the CIA used to disrupt plots and capture or kill terrorists still at large. But, those two things are inaccurate. I know it makes for a better story, but that’s just not what happened.&lt;/p&gt;
&lt;p&gt;“EITs were never used to pressure KSM to “confess” to anything…period…full stop. And, EITs were not—let me repeat— not being applied when KSM provided information that helped CIA prevent a second wave of 9⁄11 style catastrophic attacks on the West coast or aided in the capture terrorists still at large. I explain all this in my book (&lt;a href=&quot;https://amzn.to/37OUPdk&quot;&gt;Enhanced Interrogation&lt;/a&gt;)”&lt;/p&gt;
&lt;p&gt;“Gladwell makes much of KSM confessing in open court to a large variety of attacks and plots (including 9⁄11 and killing Daniel Pearl). Gladwell seems to be saying that KSM confessed to these things because, years after their brief use, the EITs compelled him to confess to things he didn’t actually do. To be clear, we did not discuss many of the things on KSM’s confessed list during his interrogations and debriefings. My guess is that if KSM confessed to crimes he didn’t commit, then it was to imbed his true crimes in a list of bogus plots in order to cast doubt on his confession later, should he need to or simply to mess with court proceedings. It is not necessary to evoke the boogie man of cognitive impairment to explain KSMs duplicitous behavior.”&lt;/p&gt;
&lt;p&gt;source: review section of &lt;a href=&quot;https://www.audible.com/pd/Talking-to-Strangers-Audiobook/1549150340&quot;&gt;https://www.audible.com/pd/Talking-to-Strangers-Audiobook/1549150340&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Andrew Ferguson&lt;/strong&gt; of The Atlantic:&lt;/p&gt;
&lt;p&gt;“Poets die young,” he writes, in a section on Sylvia Plath. “And of every occupational category, [poets] have far and away the highest suicide rates—as much as five times higher than the general population.”&lt;/p&gt;
&lt;p&gt;“To get her “five times” figure, … studied the lives of “all major British and Irish poets born between 1705 and 1805.” She determined their “major” status by consulting old poetry anthologies. She decided there were 36—not 35, not 37, but 36—major poets, ranging from …. Of the 36 poets, two committed suicide. (It’s not clear that these two can even be classified as poets, however: One was a physician by trade, and the other died at 17, probably too young to qualify for an occupational category.) Jamison reckoned that two out of 36, proportionally, is five times the suicide rate for the general population.&lt;/p&gt;
&lt;p&gt;Voilà! A statistic is born.&lt;/p&gt;
&lt;p&gt;source: &lt;a href=&quot;https://www.theatlantic.com/ideas/archive/2019/09/when-malcolm-gladwell-says-nothing-at-all/597697/&quot;&gt;https://www.theatlantic.com/ideas/archive/2019/09/when-malcolm-gladwell-says-nothing-at-all/597697/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steven Poole&lt;/strong&gt; of the Guardian:&lt;/p&gt;
&lt;p&gt;“In Gladwell’s world of large ideas, it may also be hard not to be a rapist when you’re drunk. He brings his forensic empathy to the case of Brock Turner, the Stanford college student who was caught sexually assaulting an unconscious woman on the ground outside a dorm building. Such a difficult case! Gladwell explains sorrowfully that consuming large amounts of alcohol causes mental “myopia”, where one is unable to consider the long-term consequences of one’s actions. It’s just too bad, he concludes, that these careless students both got so drunk at a party that the man could “tragically misunderstand” the woman’s intentions. You may object that plenty of men are able to get blattered without raping anyone, but that seems to be beyond Malcolm Gladwell.”&lt;/p&gt;
&lt;p&gt;source: &lt;a href=&quot;https://www.theguardian.com/books/2019/sep/11/talking-to-strangers-malcolm-gladwell-review&quot;&gt;https://www.theguardian.com/books/2019/sep/11/talking-to-strangers-malcolm-gladwell-review&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Book Club Discussion Points&lt;/h2&gt;
&lt;h3&gt;Warm-up Question&lt;/h3&gt;
&lt;p&gt;What was your most memorable encounter with strangers?&lt;/p&gt;
&lt;h3&gt;Questions&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;There are many stories in the book. Which one impacted you the most?&lt;/li&gt;
&lt;li&gt;The book is based on the ‘default to truth’ theory. Do you agree with it? Can you think of examples of when you ‘default to untruth’?&lt;/li&gt;
&lt;li&gt;Gladwell wrote, “We fall out of truth-default mode only when the case against our initial assumption becomes definitive.” Do you have good examples from your life when the truth-default scales tipped over for you?&lt;/li&gt;
&lt;li&gt;The bail judgment AI system has been shown to perform 25% better than human judges. Do you think we should reduce human judgment and increase the AI system’s role in our judicial system?&lt;/li&gt;
&lt;li&gt;The “Holy Fool” is a truth-teller because he is an outcast &amp;amp; blurts out inconvenient truths or questions things the rest of us take for granted. Have you encountered a “Holy Fool”? What inconvenient truths did they blurt out?&lt;/li&gt;
&lt;li&gt;After you read this book, what would you do differently when talking to the next stranger?&lt;/li&gt;
&lt;li&gt;Has the book changed the way you see yourself?&lt;/li&gt;
&lt;li&gt;The author wrote, “alcohol’s principal effect is to narrow our emotional and mental fields of vision. It makes the immediate aspects of the experience have a disproportionate influence on behavior and emotion.” Has this book changed your views about alcohol and its place in our culture? What do you think should be done to minimize the damage that alcohol will cause in the future?&lt;/li&gt;
&lt;li&gt;How do you make sense of the statistic that 77 out of 114 soldiers falsely identified their interrogators in a photo lineup? If torture changes the mind so much, how can we reliably get critical information from the captured enemies?&lt;/li&gt;
&lt;li&gt;515 people who tried to jump from the SF Golden Gate bridge had been unexpectedly restrained. Just 25 of them persisted in killing themselves some other way. Do you agree that suicide is coupled? Why is it tough for us to accept the idea that a behavior can be so tightly coupled to a place?&lt;/li&gt;
&lt;li&gt;Firearm suicides make two-thirds of all gun deaths and half of the suicides in the US. The US firearm suicide rate is &lt;a href=&quot;https://everytownresearch.org/firearm-suicide/#foot_note_anchor_8&quot;&gt;10 times that of other high-income countries&lt;/a&gt;. Do you think that US suicide is coupled to the firearm, or is it the other way around, that firearm suicide is coupled to the USA? What can we do about this?&lt;/li&gt;
&lt;li&gt;How has reading the book affected your views on the victimization of unarmed black people (Sandra Bland &amp;amp; Ferguson) to women &amp;amp; children being sexually assaulted at colleges (Brock Turner &amp;amp; Sandusky)? Do you agree with Gladwell that these are mere “communication” issues between strangers?&lt;/li&gt;
&lt;li&gt;Gladwell is saying that the riots in Ferguson, Mo., are not about race, but about “a particular style of policing that had been practiced in the city for years.” Police officers approach civilians on the flimsiest of pretexts, looking for a needle in a haystack, resulting in obliteration of trust between police and community. What’s your take on this issue?&lt;/li&gt;
&lt;li&gt;What problems does the author identify in our society that hasn’t been discussed?&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>33 Lessons from Talking To Strangers by Malcolm Gladwell</title><link>https://www.augusteo.com/blog/talking-to-strangers/</link><guid isPermaLink="true">https://www.augusteo.com/blog/talking-to-strangers/</guid><description>33 Lessons from Talking To Strangers by Malcolm Gladwell - a blog post by Victor Augusteo</description><pubDate>Thu, 20 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/talking-to-strangers/talking-to-strangers-202002talking-to-strangers.jpg&quot; alt=&quot;talking-to-strangers-202002talking-to-strangers.jpg&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Throughout history, battles overwhelmingly involved neighbors directly across the border because people then can only interact with those who are physically close. However, today we are now thrown into contact all the time with people whose assumptions, perspectives, and backgrounds are different from our own.&lt;/li&gt;
&lt;li&gt;Before WW2, Chamberlain misread Hitler’s intention. He was assuming that the information he got from his personal interaction with Hitler was uniquely revealing. In reality, &lt;strong&gt;the extra information actually helped to cloud his judgment&lt;/strong&gt;. He was a master politician yet failed to read someone properly.&lt;/li&gt;
&lt;li&gt;Why are the group of people you expect not to be fooled are actually the ones get fooled more?&lt;/li&gt;
&lt;li&gt;Judges make bail decisions. This is the choice of whether to jail someone until trial or to let them go free on bail. They want to look at the defendant in the eyes to detect guilt or remorse to help them decide.&lt;/li&gt;
&lt;li&gt;An AI system was trained on historical judges’ decisions based on the defendant’s age and criminal record. The AI results were far superior. The people whom the AI system released are &lt;strong&gt;25% less likely to commit a crime&lt;/strong&gt; while awaiting trial compared to the judges. The AI system couldn’t see the defendant’s facial expression and demeanor, unlike the judges. It also can’t ask additional questions. &lt;strong&gt;This turned out to be an advantage&lt;/strong&gt;. How is it that meeting a stranger can make us worse at making sense of that person?&lt;/li&gt;
&lt;li&gt;The ‘&lt;strong&gt;illusion of asymmetric insight&lt;/strong&gt;’ dictates that we are convinced that we know others better than they know us. In one experiment, researchers asked the participant to fill in the blanks to complete words e.g., TOU_ _ → could either be TOUCH or TOUGH. Participants are convinced that their choices reveal nothing about them, but other’s choices reveal much. We think we can easily see into the hearts of others based on the flimsiest of clues. We jump at the chance to judge strangers. We would never do that to ourselves, of course.&lt;/li&gt;
&lt;li&gt;Timothy Levine researched deception. He conducted an experiment where people are recorded on video and are given a chance to lie. It turns out that &lt;strong&gt;an average person could identify 54-56% of the liars&lt;/strong&gt;. Police officers, judges, and CIA officers fared no better.&lt;/li&gt;
&lt;li&gt;Enter &lt;strong&gt;“Truth-Default Theory” or TDT&lt;/strong&gt;. We are much better than chance at correctly identifying the students who are telling the truth. But we’re much worse than chance at correctly identifying the students who are lying. We have a default to truth: our operating assumption is that the people we are dealing with are honest.&lt;/li&gt;
&lt;li&gt;To &lt;strong&gt;snap out of truth-default mode&lt;/strong&gt; requires what Levine calls a &lt;strong&gt;“trigger.”&lt;/strong&gt; We fall out of truth-default mode only when &lt;strong&gt;the case against our initial assumption becomes definitive&lt;/strong&gt;. In other words, we start by believing. And &lt;strong&gt;we stop believing only when our doubts and misgivings rise to the point where we can no longer explain them away&lt;/strong&gt;. You believe someone not because you have no doubts about them. Belief is not the absence of doubt. You believe someone because you don’t have enough doubts about them. Just think about how many times you have criticized someone else, in hindsight, for their failure to spot a liar. You should have known. There were all kinds of red flags. You had doubts.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;“Holy Fool”&lt;/strong&gt; is a truth-teller because he is an outcast. Those who are not part of existing social hierarchies are free to blurt out inconvenient truths or question things the rest of us take for granted. No one says a word except a small boy, who cries out, “Look at the king! He’s not wearing anything at all!” The little boy is a Holy Fool. &lt;strong&gt;We need Holy Fools in our society&lt;/strong&gt;, from time to time. They perform a valuable role.&lt;/li&gt;
&lt;li&gt;In real life, &lt;strong&gt;lies are rare&lt;/strong&gt;. And those lies that are told are &lt;strong&gt;told by a tiny subset of people&lt;/strong&gt;. That’s why it doesn’t matter so much that we are terrible at detecting lies in real life. Defaulting to truth makes logical sense. You don’t want to second guess every single coffee shop and groceries bill for the rest of your life. If you don’t begin in a state of trust, you can’t have meaningful social encounters. Default to truth becomes an issue when we are &lt;strong&gt;forced to choose between two alternatives&lt;/strong&gt;, one of which is likely and the other of which is impossible to imagine. And in those rare instances where trust ends in betrayal, those victimized by default to truth &lt;strong&gt;deserve our sympathy&lt;/strong&gt;, not our criticism.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transparency&lt;/strong&gt; is the idea that people’s &lt;strong&gt;behavior&lt;/strong&gt; and demeanor—the way they represent themselves on the outside—provides an authentic and &lt;strong&gt;reliable window into the way they feel on the inside&lt;/strong&gt;. It is the second of the crucial tools we use to make sense of strangers. &lt;strong&gt;Transparency is a myth&lt;/strong&gt;. When a liar acts like an honest person or when an honest person acts like a liar, &lt;strong&gt;we’re flummoxed&lt;/strong&gt;. Matched people conform with our expectations. Their intentions are consistent with their behavior. The mismatched are confusing and unpredictable. &lt;strong&gt;Are we sending perfectly harmless people to prison&lt;/strong&gt; while they await trial &lt;strong&gt;simply because they don’t look right&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;The most disturbing of Tim Levine’s findings was when he showed his lying videotapes to a group of &lt;strong&gt;seasoned law-enforcement agents&lt;/strong&gt;—people with fifteen years or more of interrogation experience. On “matched” senders, the experienced interrogators were perfect. You or I would probably come in at 70 or 75 percent on that set of tapes. But everyone in Levine’s group of &lt;strong&gt;highly experienced experts got every matched sender right&lt;/strong&gt;. On mismatched senders, however, their performance was abysmal: they got 20 percent right. And on the subcategory of sincere-acting liars, they came in at 14 percent—a score so low that it ought to give chills to anyone who ever gets hauled into an interrogation room with an FBI agent.&lt;/li&gt;
&lt;li&gt;When you read through the details of the &lt;strong&gt;campus sexual-assault cases&lt;/strong&gt; that have become so depressingly common, the remarkable fact is how many involve an &lt;strong&gt;almost identical scenario&lt;/strong&gt;. A young woman and a young man meet at a party, then proceed to tragically misunderstand each other’s intentions—&lt;strong&gt;and they’re drunk&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Those who study alcohol now consider alcohol as an agent of &lt;strong&gt;myopia&lt;/strong&gt;, and what they meant by myopia is that &lt;strong&gt;alcohol’s principal effect is to narrow our emotional and mental fields of vision&lt;/strong&gt;. It makes the &lt;strong&gt;immediate aspects of experience have a disproportionate influence on behavior&lt;/strong&gt; and emotion. Alcohol makes the thing in &lt;strong&gt;the foreground even more salient&lt;/strong&gt; and the thing in the &lt;strong&gt;background less significant&lt;/strong&gt;. When alcohol peels away those longer-term constraints on our behavior, &lt;strong&gt;it obliterates our true self&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;It’s tough to tell, just by looking at someone, whether they’ve &lt;strong&gt;blacked out&lt;/strong&gt;. It’s like trying to figure out if someone has a headache exclusively from the expression on their face. &lt;strong&gt;You can do anything in a blackout that you can do when you’re drunk&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;We are failing to let women know that when they render themselves defenseless, terrible things can be done to them. Young women are getting a &lt;strong&gt;distorted message that their right to match men drink for drink is a feminist issue&lt;/strong&gt;. The real feminist message should be that when you lose the ability to be responsible for yourself, &lt;strong&gt;you drastically increase the chances that you will attract the kinds of people who don’t have your best interest at heart&lt;/strong&gt;. That’s not blaming the victim; that’s trying to prevent more victims.&lt;/li&gt;
&lt;li&gt;In one study in Orange County, California, over 1,000 drivers were diverted to a parking lot late one night. They were asked to fill out a questionnaire about their evening, then interrogated by graduate students trained in intoxication detection. How did the driver talk? Walk? Was there alcohol on their breath? Were there bottles or beer cans in their car? After the interviewers made their diagnoses, the drivers were given a blood-alcohol test. Here’s how many drunk drivers were correctly identified by the interviewers: &lt;strong&gt;20 percent&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Air Force’s SERE program—&lt;strong&gt;Survival, Evasion, Resistance, Escape&lt;/strong&gt; - aims to teach key personnel what to do if they fall into enemy hands. The subjects would be cold, hungry, forced to stand—awake—inside a box for days. Then came the interrogation. One particularly effective technique developed at SERE was “walling.” You wrap a towel around someone’s neck to support their head, then bang them up against a specially constructed wall.&lt;/li&gt;
&lt;li&gt;The point of the interrogation was &lt;strong&gt;to get the subject to talk&lt;/strong&gt;—to crack open the subject’s memory and access whatever was inside. But what if the &lt;strong&gt;process of securing compliance proved so stressful to the interviewee that it affected what he or she could actually remember&lt;/strong&gt;? In one study, participants couldn’t identify their interrogators. &lt;strong&gt;Out of the fifty-two students, twenty of them picked a doctor&lt;/strong&gt; who weren’t even involved. In any court of law, the hapless physician would end up behind bars. Another more extensive study found that &lt;strong&gt;77 out of 114 soldiers falsely identified their interrogators&lt;/strong&gt; in a photo lineup—and this was 24 hours after interrogation! When these soldiers were asked how confident they were in their responses, there was no relationship between confidence and accuracy.&lt;/li&gt;
&lt;li&gt;In his book &lt;strong&gt;Why Torture Doesn’t Work&lt;/strong&gt;, neuroscientist Shane O’Mara writes that extended sleep deprivation “might induce some &lt;strong&gt;form of surface compliance&lt;/strong&gt;“—but only at the &lt;strong&gt;cost of “long-term structural remodeling&lt;/strong&gt; of the brain systems that support the very functions that the interrogator wishes to have access to.”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Poets die young&lt;/strong&gt;. That is not just a cliché. The life expectancy of poets, as a group, trails playwrights, novelists, and nonfiction writers by a considerable margin. Something about writing poetry appears either to attract the wounded or to open new wounds.&lt;/li&gt;
&lt;li&gt;In 1962, 5,588 people in England and Wales committed suicide. Of those, 2,469—44.2 percent—did so with carbon-monoxide poisoning. It was by then the leading cause of lethal self-harm in the United Kingdom. Nothing else—not overdosing on pills or jumping off a bridge—came close.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Displacement is the&lt;/strong&gt; &lt;strong&gt;assumption that people would simply switch to another method&lt;/strong&gt;. Displacement assumes that when people think of doing something as serious as committing suicide, they are tough to stop. Blocking one option isn’t going to make much of a difference. The alternative possibility is that suicide is a behavior coupled to a particular context.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coupling is the idea that behaviors are linked to particular circumstances and conditions&lt;/strong&gt;. If suicide is coupled, then it isn’t merely the act of depressed people. It’s the act of depressed people at a particular moment of extreme vulnerability and, in combination with a specific, readily available lethal means. Suicide &lt;strong&gt;goes way up when town gas first makes its way into British homes&lt;/strong&gt;. And it comes &lt;strong&gt;plunging down as the changeover to natural gas&lt;/strong&gt; begins in the late 1960s. In that ten-year window, as town gas was being slowly phased out, thousands of deaths were prevented.&lt;/li&gt;
&lt;li&gt;Psychologist Richard Seiden. Seiden followed up on 515 people who had tried to jump from the bridge between 1937 and 1971 but had been unexpectedly restrained. Just 25 of those 515 persisted in killing themselves some other way. Overwhelmingly, the people who want to jump off the Golden Gate Bridge at a given moment want to jump off the Golden Gate Bridge &lt;strong&gt;only at that given moment&lt;/strong&gt;. So when did the municipal authority that runs the bridge finally decide to install a suicide barrier? In 2018, &lt;strong&gt;more than eighty years after the bridge opened&lt;/strong&gt;. Now, why is this? Is it because the people managing the bridge are callous and unfeeling? Not at all. It’s because &lt;strong&gt;it is really hard for us to accept the idea that a behavior can be so tightly coupled to a place&lt;/strong&gt;. In one national survey, three quarters of Americans predicted that when a barrier is finally put up on the Golden Gate Bridge, most of those who wanted to take their life on the bridge would simply take their life some other way. But that’s absolutely wrong. &lt;strong&gt;Suicide is coupled.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Law of Crime Concentration&lt;/strong&gt; says, like suicide, &lt;strong&gt;crime is tied to particular places and contexts&lt;/strong&gt;. 3.3 percent of the street segments in the city accounted for more than 50 percent of the police calls. The researcher looked at New York, Seattle, Cincinnati, Kansas City, Dallas, and Boston. And every place they looked, they saw the same thing: &lt;strong&gt;Crime in every city was concentrated in a tiny number of street segments&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;AIDS is the same. &lt;strong&gt;Fifty census tracks out of fifty thousand had over half&lt;/strong&gt; of the AIDS cases in the United States. AIDS didn’t look to him like a contagious disease roaming wildly and randomly across the land. It seemed to him like an interaction between certain kinds of people and certain particular places, an epidemic with its own internal logic.&lt;/li&gt;
&lt;li&gt;The most prominent example of how our inability to understand suicide costs lives: roughly 40,000 Americans commit suicide every year, &lt;strong&gt;half of whom do so by shooting themselves&lt;/strong&gt;. &lt;strong&gt;Handguns&lt;/strong&gt; are the suicide method of choice in the United States—and the problem with that, of course, is that handguns are uniquely deadly. Handguns are America’s town gas. &lt;strong&gt;What would happen if the U.S. somehow eradicated its leading cause of suicide&lt;/strong&gt;? It’s not hard to imagine. It would uncouple the suicidal from their chosen method. &lt;strong&gt;A very conservative estimate is that banning handguns would save 10,000 lives a year, just from thwarted suicides&lt;/strong&gt;. That’s a lot of people.&lt;/li&gt;
&lt;li&gt;Extra police patrol cars will make a difference—so long as officers took the initiative and stopped anyone they thought suspicious, got out of their cars as much as possible, and went out of their way to look for weapons. And once they’ve stopped a motorist, police officers are allowed, under the law, to search the car, so long as they have reason to believe the motorist might be armed or dangerous. And what was the principal implication of coupling? &lt;strong&gt;That law enforcement didn’t need to be bigger&lt;/strong&gt;; &lt;strong&gt;it needed to be more focused&lt;/strong&gt;. If criminals operated overwhelmingly in a few concentrated hot spots, those &lt;strong&gt;crucial parts of the city should be more heavily policed than anywhere else&lt;/strong&gt;, and the kinds of crime-fighting strategies used by police in those areas ought to be very different from those used in the vast stretches of the city with virtually no crime at all.&lt;/li&gt;
&lt;li&gt;There is something about the idea of coupling—of the notion that a stranger’s behavior is tightly connected to place and context—that &lt;strong&gt;eludes us&lt;/strong&gt;. &lt;strong&gt;It leads us to misunderstand&lt;/strong&gt; some of our greatest poets, to be indifferent to the suicidal, and to send police officers on senseless errands.&lt;/li&gt;
&lt;li&gt;The anger in Ferguson wasn’t just about Brown’s death—or even mainly about Brown. It was, instead, &lt;strong&gt;about a particular style of policing that had been practiced in the city for years&lt;/strong&gt;. The Ferguson Police Department was the gold standard of Kansas City policing. It was a place where the entire philosophy of law enforcement was to stop as many people as possible for as many reasons as possible. A police officer approaches a civilian on the flimsiest of pretexts, &lt;strong&gt;looking for a needle in a haystack&lt;/strong&gt;—with the result that &lt;strong&gt;so many innocent people are caught up in the wave of suspicion that trust between police and community is obliterated&lt;/strong&gt;. That’s what was being protested in the streets of Ferguson: years and years of police officers mistaking a basketball player for a pedophile. &lt;strong&gt;They instructed their police officers to disregard their natural inclination to default to the truth—and start imagining the worst&lt;/strong&gt;: that young women coming from job interviews might be armed and dangerous, or young men cooling off after a pickup game might be pedophiles.&lt;/li&gt;
&lt;li&gt;How many extra guns and drugs did the North Carolina Highway Patrol find with those 400,000 searches? &lt;strong&gt;Seventeen&lt;/strong&gt;. Is it really worth alienating and stigmatizing 399,983 Mikes and Sandras to find 17 bad apples?&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>The Best Biography Books I Read in the Last Decade (2010-2019)</title><link>https://www.augusteo.com/blog/best-decade-biography-books/</link><guid isPermaLink="true">https://www.augusteo.com/blog/best-decade-biography-books/</guid><description>The Best Biography Books I Read in the Last Decade (2010-2019) - a blog post by Victor Augusteo</description><pubDate>Wed, 29 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-biography-books/best-decade-biography-books-202001freestocks-org-OfaDD5o8hpk-unsplash.jpg&quot; alt=&quot;best-decade-biography-books-202001freestocks-org-OfaDD5o8hpk-unsplash.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This article a follow up on the &lt;a href=&quot;https://hugo.augusteo.com/blog/i-read-523-books-last-decade.-here-are-some-fun-stats/&quot;&gt;previous post about my last decade’s reading statistic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I decided to break the book recommendation post into three sections because it has grown too long. The three parts are &lt;a href=&quot;https://hugo.augusteo.com/blog/the-best-non-fiction-books-i-read-in-the-last-decade-2010-2019/&quot;&gt;non-fiction&lt;/a&gt;, &lt;a href=&quot;https://hugo.augusteo.com/blog/the-best-fiction-books-i-read-in-the-last-decade-2010-2019/&quot;&gt;fiction&lt;/a&gt;, and biography (this post).&lt;/p&gt;
&lt;p&gt;These are the books that I believe have significantly affected me and changed my life for the better.&lt;/p&gt;
&lt;p&gt;Sorted in no particular order. However, If you only have time to read one book in that category, pick the top.&lt;/p&gt;
&lt;h3&gt;Business&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-biography-books/best-decade-biography-books-202001stevejobs.jpg&quot; alt=&quot;best-decade-biography-books-202001stevejobs.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2RusHHm&quot;&gt;Steve Jobs&lt;/a&gt; by Walter Isaacson walks you through Jobs’ life and gives you a glimpse of how he operates and what motivates him. The stories are based on actual interviews with him and people close to him. The story is filled with lessons about innovation, leadership, and a revelation of how terrible he treated the people who work with him.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2RtbVIt&quot;&gt;Shoe Dog: A Memoir by the Creator of NIKE&lt;/a&gt; by Phil Knight is a riveting tale about the start and growth of Nike and Phil Knight. He started really small with only fifty dollars of loans and was able to take the world by storm. But it wasn’t always easy. He had to navigate politics across cultures and make lots of smart decisions. Great read overall.&lt;/p&gt;
&lt;h3&gt;Physical Feat&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-biography-books/best-decade-biography-books-202001endurance.jpg&quot; alt=&quot;best-decade-biography-books-202001endurance.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/3aJ5pFe&quot;&gt;Endurance: Shackleton’s Incredible Voyage&lt;/a&gt; by Alfred Lansing shows you the peak of human perseverance in the face of almost-certain death. Shackleton’s crew was trapped in Antarctica and had to trek almost a thousand miles in freezing cold conditions. They had to focus one hundred percent on survival.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/30Y5dNK&quot;&gt;Open&lt;/a&gt; by Andre Agassi tells a gripping tale of how Agassi was raised. Since very young, he had been trained with &lt;em&gt;the dragon&lt;/em&gt;, a cannon that shoots out tennis balls. He had a very complicated relationship with the game of tennis, and you can tell that he doesn’t really enjoy it all that much. This might be a cautionary tale of all the tiger parents out there. You can help your kids to be successful, but at what cost? Is it worth it?&lt;/p&gt;
&lt;h3&gt;Science &amp;amp; Technology&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-biography-books/best-decade-biography-books-202001feynman.jpg&quot; alt=&quot;best-decade-biography-books-202001feynman.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2t0gbWJ&quot;&gt;Surely You’re Joking, Mr. Feynman!: Adventures of a Curious Character&lt;/a&gt; by Richard P. Feynman is a must-read for everyone. He had a way of just having fun in everything he did. This book shows how curious and playful he was even while working on the nuclear bomb project and cracking safes. I certainly look up to him a lot and hope to emulate how he blitzed through life with such energy.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2RuEANC&quot;&gt;Just for Fun: The Story of an Accidental Revolutionary&lt;/a&gt; by Linus Torvalds is a refreshing approach to the Linux systems and how it came to be. For those who don’t know, Linux is an operating system that runs virtually all of the internet services that you use on a day to day basis. Linus was never in it for fame and glory. Instead, it was all for fun. Even now, he stills writes and improves on the codes of his creations.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/36uWpjM&quot;&gt;An Astronaut’s Guide to Life on Earth&lt;/a&gt; by Chris Hadfield will give you a deep insight into astronauts and their lives. The parts that struck me most was the loneliness and isolation before launch. He couldn’t even be in the same room as his kids to avoid the risk of getting sick in space. You will learn a lot about his life and the process of becoming an astronaut.&lt;/p&gt;
&lt;h3&gt;Foreign Culture&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-biography-books/best-decade-biography-books-202001educated.jpg&quot; alt=&quot;best-decade-biography-books-202001educated.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/38IaNqg&quot;&gt;Educated&lt;/a&gt; by Tara Westover gives us a window into the hard the life of survivalist and anti-government family would be. Her dad isolated the family, and the kids grew up without the normalcy that almost all of us enjoys. Despite the hardship, Tara managed to get high education at Harvard and Cambridge by her hard work and a fair amount of luck. It makes you wonder how many brilliant kids are there in the world, just waiting for a sliver of a chance.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2vkQII9&quot;&gt;Maus Series&lt;/a&gt; by Art Spiegelman is a Pulitzer prize-winning graphic novel depicting the life and suffering of the Jews in the hand of Nazi Germany. The stories are very personal, intimate, and told by a survivor of the genocide. The drawings are simple but meaningful, with mice representing the Jews and cats representing the Nazis. You will learn just how violent and evil we humans can be.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/37Jgrsp&quot;&gt;Born a Crime: Stories From a South African Childhood&lt;/a&gt; by Trevor Noah tells the story of growing up in apartheid South Africa and its hardship. Being born into a mixed-race brings about a complication of belonging. He is neither white nor black. You will read about the mischievousness of kids and just how they adapt to their environment despite ostracism and other evils. This is a funny and personal book that will make you laugh and cry.&lt;/p&gt;
&lt;h3&gt;Inspirational&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-biography-books/best-decade-biography-books-202001permanentrecord.jpg&quot; alt=&quot;best-decade-biography-books-202001permanentrecord.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2RUT8Vt&quot;&gt;Permanent Record&lt;/a&gt; by Edward Snowden reveals the extent of spying by the US government and how this one lone whistleblower put his life in jeopardy to inform the world. He couldn’t live with the disillusionment with how the government is treating its citizens. He chose to abandon a great job with a high salary, a loving partner, and stick to his life principles. The PRISM program has recorded and kept private information, photos, and webcam recording of millions of people for an indefinite time. Reading this will prompt you to take more control of your online privacy, but at times it will feel like Sisyphus.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/3aQhj0j&quot;&gt;Not Fade Away: A Memoir of Senses Lost and Found&lt;/a&gt; by Rebecca Alexander will show you the world of someone who is losing both their sense of sight and hearing due to genetic disease. On top of that, she even broke her legs. Despite all the hardship, she still managed to complete two master’s degrees, competed in endurance sports, and became a physiotherapist to help others to recover their mobility. Can you imagine what your life would be like if you are losing both your sight and hearing?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2GuAC0Q&quot;&gt;A Long Way Home&lt;/a&gt; by Saroo Brierley has been made into a Hollywood movie ‘&lt;em&gt;Lion&lt;/em&gt;,’ and you’ve probably watched it. It is a tale about a lost Indian boy who was forced to survive on the streets. Later he was adopted by an Australian family. When he grew up, he used Google Maps and what recollection he had to find his original hometown. Miraculously, he managed to do just that.&lt;/p&gt;
&lt;h3&gt;Politics&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-biography-books/best-decade-biography-books-202001higherloyalty.jpg&quot; alt=&quot;best-decade-biography-books-202001higherloyalty.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2RtvW1z&quot;&gt;A Higher Loyalty: Truth, Lies, and Leadership&lt;/a&gt; by James Comey is an honest and personal story by the former FBI director who was fired by Trump. In this book, you will learn how the court system and FBI works. He put a lot of care not to make any partisan decision and stick to his own principles. As a result, he ultimately lost his job. This book contains excellent lessons about loyalty, leadership, and integrity.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2U62RLk&quot;&gt;Free to Be Ruth Bader Ginsburg: The Story of Women and Law&lt;/a&gt; by Teri Kanefield is actually the first time I’ve ever heard of RBG since I didn’t grow up in the US. She brought about a lot of change in the court system regarding women empowerment. The book starts from her childhood and her family, then progresses to her early career and marriage, and ultimately to her supreme court appointment and the subsequent cases.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/38746485-becoming&quot;&gt;Becoming&lt;/a&gt; by Michelle Obama feels like a sincere telling of the former first lady’s life. She didn’t pull any punches. The book described both the good and the bad things alike. It starts from her childhood on the South Side of Chicago, her impressive careers, meeting Barrack, and ends with her endeavors as the first lady of the US. I especially love learning about the road to the presidency, and the tolls than campaigning will take.&lt;/p&gt;
</content:encoded></item><item><title>The Best Fiction Books I Read in the Last Decade (2010-2019)</title><link>https://www.augusteo.com/blog/best-decade-fiction-books/</link><guid isPermaLink="true">https://www.augusteo.com/blog/best-decade-fiction-books/</guid><description>The Best Fiction Books I Read in the Last Decade (2010-2019) - a blog post by Victor Augusteo</description><pubDate>Fri, 24 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-fiction-books/best-decade-fiction-books-202001natalia-y-x69K221AGHw-unsplash.jpg&quot; alt=&quot;best-decade-fiction-books-202001natalia-y-x69K221AGHw-unsplash.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This article a follow up on the &lt;a href=&quot;https://hugo.augusteo.com/blog/i-read-523-books-last-decade.-here-are-some-fun-stats/&quot;&gt;previous post about my last decade’s reading statistic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I decided to break the book recommendation post into three sections because it has grown too long. The three parts are &lt;a href=&quot;https://hugo.augusteo.com/blog/the-best-non-fiction-books-i-read-in-the-last-decade-2010-2019/&quot;&gt;non-fiction&lt;/a&gt;, fiction (this post), and &lt;a href=&quot;https://hugo.augusteo.com/blog/the-best-biography-books-i-read-in-the-last-decade-2010-2019/&quot;&gt;biographies&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These are the books that I believe have significantly affected me and changed my life for the better.&lt;/p&gt;
&lt;p&gt;Sorted in no particular order. However, If you only have time to read one book in that category, pick the top.&lt;/p&gt;
&lt;h3&gt;Fan Fiction&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-fiction-books/best-decade-fiction-books-202001hpmor.jpg&quot; alt=&quot;best-decade-fiction-books-202001hpmor.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/30UFXZ3&quot;&gt;&lt;strong&gt;Harry Potter and the Methods of Rationality&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Eliezer Yudkowsky&lt;/strong&gt; is the best fanfiction I’ve read in the last decade. What if Harry Potter is trained in the methods of rationality? What if instead of blindly following the teachers, the kids actually investigate the magic system themselves. He also fixed some of the more obvious plot holes like Hermoine joining Ravenclaw instead of Griffyndor like the original.&lt;/p&gt;
&lt;p&gt;This is required reading for anyone who loves the Harry Potter series or wants to be more rational. You can download the ebook for free here on &lt;a href=&quot;https://www.hpmor.com/&quot;&gt;the official website&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;High Fantasy&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-fiction-books/best-decade-fiction-books-202001nameofthewind.jpg&quot; alt=&quot;best-decade-fiction-books-202001nameofthewind.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/36tGjqv&quot;&gt;&lt;strong&gt;The Kingkiller Chronicle Series&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Patrick Rothfuss&lt;/strong&gt; is required reading for fantasy lovers. The story will grip you from start to finish, and you will have an intense love-hate relationship with the characters and appreciation for the silence of three parts.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/3aN9RTh&quot;&gt;&lt;strong&gt;Mistborn Series&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Brandon Sanderson&lt;/strong&gt; should be the entry point for people who wants to get into fantasy. The original trilogy is excellent, and the subsequent books build upon the world. It has a cohesive world-building and magic system, believable character development, and storytelling that will engage you throughout.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2RVcnOX&quot;&gt;&lt;strong&gt;The Stormlight Archive Series&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Brandon Sanderson&lt;/strong&gt; is his latest epic fantasy that follows Kaladin in a world torn apart by the globe-circling storm. Unlike Mistborn, that’s mainly focused on the main character, this one will dazzle you with an array of seemingly unrelated characters that will work together to save the world.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/38JOErw&quot;&gt;&lt;strong&gt;The Lies of Locke Lamora&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Scott Lynch&lt;/strong&gt; is a unique book because it focuses on mind-games and lies. You follow the life of the con artist Locke and his exploits to lie and cheat his way through life.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2RuxzfO&quot;&gt;&lt;strong&gt;A Song of Ice and Fire Series&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by George R.R. Martin&lt;/strong&gt; is the source material to the world-famous Game of Thrones show by HBO. The books are better. Be prepared to whip up pen and paper so you can keep track of the hundreds of characters and their relationships.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2sYCb48&quot;&gt;&lt;strong&gt;The Witcher Saga&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Andrzej Sapkowski&lt;/strong&gt; is now an ongoing TV series by Netflix. It has also spawned 3+ best selling video games with Witcher 3 winning so many awards. Witchers earn a living by hunting monsters. They do not rely on muscle alone but rely more on their expansive detailed knowledge on the enemy’s strengths, and weaknesses and how to best defeats them. They bring rationality in a world beset by superstitions.&lt;/p&gt;
&lt;h3&gt;General Fantasy&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-fiction-books/best-decade-fiction-books-202001oceanattheendofthelane.jpg&quot; alt=&quot;best-decade-fiction-books-202001oceanattheendofthelane.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/38KQMzE&quot;&gt;&lt;strong&gt;The Ocean at the End of the Lane&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Neil Gaiman&lt;/strong&gt; is a fantastic fantasy that is set in a small town, not unlike the one you grew up on. The magic and supernatural events hit you from a blind spot. You wouldn’t expect them.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/37qc1q1&quot;&gt;&lt;strong&gt;Watership Down&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Richard Adams&lt;/strong&gt; is now a Netflix series. It is an adult fantasy series about talking bunnies. One of the rabbits had a prophecy about impending doom, and a group set out to find a new burrow to avoid said doom. This is filled with intrigue, deception, betrayals, brutal murders, deposing authoritarian regimes, and calling in airstrikes. Definitely, do not read this story to your child.&lt;/p&gt;
&lt;h3&gt;Science Fiction&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-fiction-books/best-decade-fiction-books-202001threebodyproblem.jpg&quot; alt=&quot;best-decade-fiction-books-202001threebodyproblem.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/38HSMs8&quot;&gt;&lt;strong&gt;Remembrance of Earth’s Past Series&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Liu Cixin&lt;/strong&gt; is the best SciFi series I’ve read. It started with stories of scientists driven mad to suicide, and it ends with a universe-wide extinction event. You will learn about intergalactic politics and its allegory to our current nuclear deterrence, love across space-time, and mind-bending dimension-folding technologies. A must-read.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2RtB9GH&quot;&gt;&lt;strong&gt;Chaos Walking Series&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Patrick Ness&lt;/strong&gt; tells the story of a spacefaring human colony that settled on a planet where some virus made it, so men’s thoughts are broadcasted to all women. This created extreme information imbalance, and the societies dealt with this in different ways. You will meet weird aliens and cross-species friendships.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2O2wKbF&quot;&gt;&lt;strong&gt;The Origin Mystery Series&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by A.G. Riddle&lt;/strong&gt; is a story of a weaponized global pandemic, Atlantis technologies, secret societies, and conspiracies.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/30W9fX2&quot;&gt;&lt;strong&gt;Artemis&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Andy Weir&lt;/strong&gt; shows you how a colony in a moon might be like. You follow a little girl and her shenanigans while uncovering sinister plots that will affect the whole settlement.&lt;/p&gt;
&lt;h3&gt;Mythology&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-fiction-books/best-decade-fiction-books-202001norsemythology.jpg&quot; alt=&quot;best-decade-fiction-books-202001norsemythology.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/3aKNlut&quot;&gt;&lt;strong&gt;Norse Mythology&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Neil Gaiman&lt;/strong&gt; is a refreshing retelling of the old tales. You will learn about the gods, giants, and their relationships.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/30WPOgN&quot;&gt;&lt;strong&gt;Mythos: The Greek Myths Retold&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Stephen Fry&lt;/strong&gt; and &lt;a href=&quot;https://amzn.to/38L1eqP&quot;&gt;&lt;strong&gt;Circe&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Madeline Miller&lt;/strong&gt; are also modern retell just like the one above. I always love the intrigues of the Olympians.&lt;/p&gt;
&lt;h3&gt;Historical Fiction&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-fiction-books/best-decade-fiction-books-202001pachinko.jpg&quot; alt=&quot;best-decade-fiction-books-202001pachinko.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2U5KUMW&quot;&gt;&lt;strong&gt;Pachinko&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Min Jin Lee&lt;/strong&gt; is an epic story spanning generations and two countries. A Korean family is dealing with the Japanese invasion. You will learn the atrocities committed and how the civilians handle it. In case you still haven’t figured it out, the story is a sad one.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2O1yvWS&quot;&gt;&lt;strong&gt;Shōgun&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by James Clavell&lt;/strong&gt; is a classic tale about Westerners who crash-landed in Japan during the shogunate period. The newcomers will have to learn the local’s way of life and vice versa. This is a tale of battle, subversion, politics, and love.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2S4R3GX&quot;&gt;&lt;strong&gt;Shantaram&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Gregory David Roberts&lt;/strong&gt; is about an Australian who flees to India after breaking out of prison. There he undergoes a journey of survival and self-discovery. You will meet local underbellies, police corruption, how the poor helps each other, and many more.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/37t1I4s&quot;&gt;&lt;strong&gt;The Pillars of the Earth&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Ken Follett&lt;/strong&gt; will walk you through the struggle of building a cathedral in medieval times. You would think such a tale be boring, but that couldn’t be further from the truth. Follett wrote with such prose that you can’t help but feel like you are experiencing the journey firsthand.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/36rpgFt&quot;&gt;&lt;strong&gt;All the Light We Cannot See&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Anthony Doerr&lt;/strong&gt; allows you to live in the eyes of a little girl and a man caught in World War II fights. A very moving story.&lt;/p&gt;
&lt;h3&gt;Literary Fiction&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-fiction-books/best-decade-fiction-books-202001howtogetfilthyrichinrisingasia.jpg&quot; alt=&quot;best-decade-fiction-books-202001howtogetfilthyrichinrisingasia.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2NYyP8r&quot;&gt;&lt;strong&gt;How to Get Filthy Rich in Rising Asia&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Mohsin Hamid&lt;/strong&gt; is the only book I’ve read that’s written in second person style. You reread that sentence in confusion, surely that’s a mistake. This prompted you to click that link and read the reviews for yourself and decided to get the book. The story impressed you as you follow the life of a man born in a poor household who rises through the ranks to become a rich man. But for what? What does he want in the end? You loved this book and told everyone you know to read this.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/37xlFHp&quot;&gt;&lt;strong&gt;Where the Crawdads Sing&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Delia Owens&lt;/strong&gt; shows you the life of a girl who lived in a swamp. She ended up studying by herself through books and becomes a best-selling natural sciences author. Later she gets tangled in a murder case that pits herself against an entire town who hates and calls her the &lt;em&gt;swamp girl&lt;/em&gt;. Did she do it?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2Rsaf20&quot;&gt;&lt;strong&gt;The Overstory&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Richard Powers&lt;/strong&gt; starts with a collection of many seemingly unrelated stories that somehow ended up converging at the end. A group of eco-terrorist is trying to stop a logging company from cutting down ancient trees and accidentally killed one of their members. What are they going to do? You will learn about the hidden world of trees.&lt;/p&gt;
&lt;h3&gt;Graphic Novel&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-fiction-books/best-decade-fiction-books-202001habibi.jpg&quot; alt=&quot;best-decade-fiction-books-202001habibi.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/310OlpJ&quot;&gt;&lt;strong&gt;Habibi&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Craig Thompson&lt;/strong&gt; really impressed me. The Arabian story feels really authentic and alive. Do not give this book to your kids. This is for adults only. There are lots of sex, violence, and magic.&lt;/p&gt;
</content:encoded></item><item><title>The Best Non-Fiction Books I Read in the Last Decade (2010-2019)</title><link>https://www.augusteo.com/blog/best-decade-non-fiction-books/</link><guid isPermaLink="true">https://www.augusteo.com/blog/best-decade-non-fiction-books/</guid><description>The Best Non-Fiction Books I Read in the Last Decade (2010-2019) - a blog post by Victor Augusteo</description><pubDate>Sun, 19 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-non-fiction-books/best-decade-non-fiction-books-202001person-reading-book-and-holding-coffee-1550648.jpg&quot; alt=&quot;best-decade-non-fiction-books-202001person-reading-book-and-holding-coffee-1550648.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This article a follow up on the &lt;a href=&quot;https://hugo.augusteo.com/blog/i-read-523-books-last-decade.-here-are-some-fun-stats/&quot;&gt;previous post about my last decade’s reading statistic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I decided to break the book recommendation post into three sections because it has grown too long. The three parts are non-fiction (this post), &lt;a href=&quot;https://hugo.augusteo.com/blog/the-best-fiction-books-i-read-in-the-last-decade-2010-2019/&quot;&gt;fiction&lt;/a&gt;, and &lt;a href=&quot;https://hugo.augusteo.com/blog/the-best-biography-books-i-read-in-the-last-decade-2010-2019/&quot;&gt;biographies&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These are the books that I believe have significantly affected me and changed my life for the better.&lt;/p&gt;
&lt;p&gt;Sorted in no particular order. However, If you only have time to read one book in that category, pick the top.&lt;/p&gt;
&lt;h3&gt;Philosophy&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-non-fiction-books/best-decade-non-fiction-books-20200117173Hedc4ZL.jpg&quot; alt=&quot;best-decade-non-fiction-books-20200117173Hedc4ZL.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/37sGcgo&quot;&gt;&lt;strong&gt;How to Think Like a Roman Emperor: The Stoic Philosophy of Marcus Aurelius&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Donald J. Robertson&lt;/strong&gt; is the best step by step guide to Stoicism that I’ve ever read. If we all could learn to not over-react to adversity, there would be much fewer wars and suffering. This book is essentially a modernized form of &lt;a href=&quot;https://amzn.to/2RQOFDx&quot;&gt;&lt;strong&gt;Meditations&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Marcus Aurelius&lt;/strong&gt;. I plan to reread these two books again and again.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2RuB3ik&quot;&gt;&lt;strong&gt;Free Will&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://amzn.to/2t3V068&quot;&gt;&lt;strong&gt;The Moral Landscape: How Science Can Determine Human Values&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Sam Harris&lt;/strong&gt; heavily influenced how I view decision making and definitely improved my empathy. If free will is indeed an illusion, then how do we improve our lives and those we love? Without free will, those who made sub-par decisions don’t really have a choice. Next time when someone did something you think stupid, try to imagine that if you were born and raised in their exact environment and family, you would most certainly make the same decision that you considered stupid before.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2GrYi6g&quot;&gt;&lt;strong&gt;Waking Up: A Guide to Spirituality Without Religion&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Sam Harris&lt;/strong&gt; and &lt;a href=&quot;https://amzn.to/37vAEBI&quot;&gt;&lt;strong&gt;In Love with the World: A Monk’s Journey Through the Bardos of Living and Dying&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Yongey Mingyur Rinpoche&lt;/strong&gt; showed me the benefits &amp;amp; practicality of meditation. It is a wonder that the majority of the world’s population live their lives without ever thinking about how their mind works. Mindfulness meditation is surprisingly easy to learn and really helps you to understand what actually happens in your consciousness.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/30XqoQe&quot;&gt;&lt;strong&gt;A Manual for Creating Atheists&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Peter Boghossian&lt;/strong&gt; gets an honorable mention here. The book taught me about the Socratic method and its application. You don’t have to go out and try to deconvert everyone you met. Just knowing the technique is absolutely beneficial, since you can apply the same principle to your beliefs to find out which ones should be dropped or kept.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2GmXLlY&quot;&gt;&lt;strong&gt;Zen and the Art of Motorcycle Maintenance: An Inquiry Into Values&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Robert M. Pirsig&lt;/strong&gt; is a weird &amp;amp; polarizing book. It asks a lot of abstract questions like “What is quality?” and that makes people uncomfortable. However, if you do enjoy learning philosophy, this book would be a good step out of your comfort zone.&lt;/p&gt;
&lt;h3&gt;Sciences&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-non-fiction-books/best-decade-non-fiction-books-2020011A1xkFZX5k-L.jpg&quot; alt=&quot;best-decade-non-fiction-books-2020011A1xkFZX5k-L.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2U2BUbx&quot;&gt;&lt;strong&gt;A Brief History of Time&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Stephen Hawking&lt;/strong&gt; is a fun little book. I believe this is a must-read for everybody to learn about our home and the basics of how it works.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/30VSKKF&quot;&gt;&lt;strong&gt;The Design of Everyday Things&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Donald A. Norman&lt;/strong&gt; showed me the hidden world of design. Steve Jobs would have probably recommended this book as well. You will learn about the designs of the objects you interact with every day and why it is so.&lt;/p&gt;
&lt;h3&gt;Communication &amp;amp; Negotiation&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-non-fiction-books/best-decade-non-fiction-books-202001141fi2jfAD3L.jpg&quot; alt=&quot;best-decade-non-fiction-books-202001141fi2jfAD3L.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2GqQAJK&quot;&gt;&lt;strong&gt;Fearless Salary Negotiation: A step-by-step guide to getting paid what you’re worth&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Josh Doody&lt;/strong&gt; taught me how to negotiate and gave me the confidence actually to do it in high stakes salary negotiation. This book would directly make you more money than the others.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2GqURwC&quot;&gt;&lt;strong&gt;Never Split the Difference: Negotiating As If Your Life Depended On It&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Chris Voss&lt;/strong&gt; is another great negotiation book that isn’t just about salary.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2RvLthm&quot;&gt;&lt;strong&gt;Non Violent Communication&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Marshall B. Rosenberg&lt;/strong&gt; will save &amp;amp; improve your relationship with others. We should all learn to communicate without causing further enmity.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/30XqM18&quot;&gt;&lt;strong&gt;The Game: Penetrating the Secret Society of Pickup Artists&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Neil Strauss&lt;/strong&gt; tells an amazing tale of a shy journalist’s journey in the world of people who specialized in pick-up, which really is just communication and negotiation. If you are the type of person who thinks that the pick-up community is toxic, just try to imagine this as an anthropology book where an Alien came to earth to learn about homo sapiens’ mating behavior. After all, pick-ups are just communications.&lt;/p&gt;
&lt;h3&gt;Anthropology &amp;amp; History&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-non-fiction-books/best-decade-non-fiction-books-202001171N3-FFSDxL.jpg&quot; alt=&quot;best-decade-non-fiction-books-202001171N3-FFSDxL.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/37yyXDq&quot;&gt;**Sapiens: A Brief History of Humankind&lt;/a&gt;,** &lt;a href=&quot;https://amzn.to/2RwJObI&quot;&gt;&lt;strong&gt;Homo Deus: A History of Tomorrow&lt;/strong&gt;&lt;/a&gt; &amp;amp; &lt;a href=&quot;https://amzn.to/30VSXxr&quot;&gt;&lt;strong&gt;21 Lessons for the 21st Century&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Yuval Noah Harari&lt;/strong&gt; is a must-read trilogy. He went into great detail of our species’ past, present and future. Sapiens talks about our species’ history, Homo Deus predicts our future, and the 21 Lessons combines them both to help us prepare.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2sYAovW&quot;&gt;&lt;strong&gt;Guns, Germs, and Steel: The Fates of Human Societies&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Jared Diamond&lt;/strong&gt; has a similar tone to Harrari’s but focuses more on why some nation advances quicker than others. This will probably help to dispel the notion that some race’s skin color made certain people superior to others. The biggest difference-maker is really luck that has been compounded over generations.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2Gwf0Bf&quot;&gt;&lt;strong&gt;Scale: The Universal Laws of Growth, Innovation, Sustainability, and the Pace of Life in Organisms, Cities, Economies, and Companies&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Geoffrey B. West&lt;/strong&gt; will blow your mind. Everything is a system, and each system scales according to a set of power laws. Once you have learned this concept, a lot of things will make more sense. This is a must-read for everyone who works with any kind of system.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2tKiayS&quot;&gt;&lt;strong&gt;Enlightenment Now: The Case for Reason, Science, Humanism, and Progress&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Steven Pinker&lt;/strong&gt; tells the story that despite the bad news every day, our society is actually progressing positively at a rapid rate. Pinker presents his view with a plethora of charts and data.&lt;/p&gt;
&lt;h3&gt;Parenting&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-non-fiction-books/best-decade-non-fiction-books-202001181S8Jf6iWFL.jpg&quot; alt=&quot;best-decade-non-fiction-books-202001181S8Jf6iWFL.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2RY6lx8&quot;&gt;&lt;strong&gt;The Self-Driven Child: The Science and Sense of Giving Your Kids More Control Over Their Lives&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by William Stixrud&lt;/strong&gt; and &lt;a href=&quot;https://amzn.to/2uCfd3a&quot;&gt;&lt;strong&gt;The Vanishing American Adult: Our Coming-of-Age Crisis—and How to Rebuild a Culture of Self-Reliance&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Ben Sasse&lt;/strong&gt; shows a better way to raise our kids in today’s age. We shouldn’t coddle our children and protect them from failure. Instead, we should let them fail early and often to assist learning. Despite lowering crime and kidnapping rates, parents nowadays are ever more afraid to let their children play by themselves. I certainly plan to give my kids control more of their own lives more.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/30Y1zDW&quot;&gt;&lt;strong&gt;How to Talk So Kids Will Listen &amp;amp; Listen So Kids Will Talk&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Adele Faber&lt;/strong&gt; will teach you how to communicate better not just to your kids but also to other adults.&lt;/p&gt;
&lt;h3&gt;Software Engineering&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-non-fiction-books/best-decade-non-fiction-books-202001181R20ex0HOLL.jpg&quot; alt=&quot;best-decade-non-fiction-books-202001181R20ex0HOLL.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/37wcs1Y&quot;&gt;&lt;strong&gt;Algorithms to Live By: The Computer Science of Human Decisions&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Brian Christian&lt;/strong&gt; shows you how learning the basics of algorithms can help you make decisions in day to day life. I highly recommend this to both technical and non-tech people alike. Learn about the theory of ‘explore’ vs. ‘exploit’ and caching with many real-life examples.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2GsmiGe&quot;&gt;&lt;strong&gt;Cracking the Coding Interview: 150 Programming Questions and Solutions&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Gayle Laakmann McDowell&lt;/strong&gt; helped me to get my current job in Silicon Valley. If you want to play the game, you got to learn the rules. This book is the best guide to get a tech job. The tech interview is often viewed as broken, but all the top companies still do it. It is so bad that even if you work with a complex system every day, you will always have to study for interviews. I do hope this trend changes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2sZMKE5&quot;&gt;&lt;strong&gt;RxSwift: Reactive Programming with Swift&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Florent Pillet&lt;/strong&gt; will teach you Functional Reactive Programming (FRP) for iOS. Once you grok FRP, you won’t want to use the old callback pattern anymore.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/36sSyDT&quot;&gt;&lt;strong&gt;The Mythical Man-Month: Essays on Software Engineering&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Frederick P. Brooks Jr.&lt;/strong&gt; will show you the pain and suffering of scheduling and running software projects. This is a classic and required reading for project managers.&lt;/p&gt;
&lt;h3&gt;Leadership&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-non-fiction-books/best-decade-non-fiction-books-202001171GqMTCFWtL.jpg&quot; alt=&quot;best-decade-non-fiction-books-202001171GqMTCFWtL.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2GqkpKj&quot;&gt;&lt;strong&gt;Extreme Ownership: How U.S. Navy Seals Lead and Win&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Jocko Willink&lt;/strong&gt; is a brilliant book. Plenty of Navy Seals war stories juxtaposed with business scenarios. A mission or project can only be successful if someone takes extreme ownership. This is a highly recommended book for anyone who leads other people.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/3aPXfLx&quot;&gt;&lt;strong&gt;The Dichotomy of Leadership: Balancing the Challenges of Extreme Ownership to Lead and Win&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Jocko Willink&lt;/strong&gt; is a followup book that answers the questions of ‘how much is too extreme?’. You should only read this after reading the first one.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/37sHmbK&quot;&gt;&lt;strong&gt;Radical Candor: Be a Kickass Boss Without Losing Your Humanity&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Kim Malone Scott&lt;/strong&gt; is a refreshing take on leadership without any bullshit. Just try to be honest and communicate more without being an asshole.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2GugFqY&quot;&gt;&lt;strong&gt;The Manager’s Path: A Guide for Tech Leaders Navigating Growth and Change&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Camille Fournier&lt;/strong&gt; is a book specifically for the tech industry. Highly recommended for anyone who wanted to transition or found themselves transitioning from individual contributor to leadership roles.&lt;/p&gt;
&lt;h3&gt;Medical &amp;amp; Health&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-non-fiction-books/best-decade-non-fiction-books-202001181puhCkxpbL.jpg&quot; alt=&quot;best-decade-non-fiction-books-202001181puhCkxpbL.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2GCyHYx&quot;&gt;&lt;strong&gt;Why We Sleep: Unlocking the Power of Sleep and Dreams&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Matthew Walker&lt;/strong&gt; will convince you to prioritize sleep. You will learn the downsides of lack of sleep, the benefit of sleeping more, and the techniques to get better sleep (a.k.a. sleep hygiene).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/30UMOS0&quot;&gt;&lt;strong&gt;Being Mortal: Medicine and What Matters in the End&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Atul Gawande&lt;/strong&gt; discusses our mortality and the sorry state of end-of-life treatments. We will all die sooner or later. How should we handle all this and plan properly?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/310Tj66&quot;&gt;&lt;strong&gt;The Emperor of All Maladies: A Biography of Cancer&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Siddhartha Mukherjee&lt;/strong&gt; will teach you about the history of cancer and how badly our doctors ignored data about the screw-ups. We have come a long way, and we might have a lot longer way to go.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2uxfLHI&quot;&gt;&lt;strong&gt;How to Change Your Mind: What the New Science of Psychedelics Teaches Us About Consciousness, Dying, Addiction, Depression, and Transcendence&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Michael Pollan&lt;/strong&gt; is a must-read if you are interested in how psychedelics medicines might be beneficial to us. The classification of psychedelics as an illegal drug has virtually stopped all research for decades. Only now have scientists started to look into it again. I wouldn’t be surprised if we would all be taking LSD or magic mushrooms as treatment or preventions for mental ailments in ten years.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2TYDdbC&quot;&gt;&lt;strong&gt;The Epigenetics Revolution&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Nessa Carey&lt;/strong&gt; is a must-read if you are interested in genetics but do not know what epigenetics is. Fundamentally, genes can be turned on and off depending on an individual’s environments. A highly engaging book filled with real-life scenarios.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2O1nEff&quot;&gt;&lt;strong&gt;The Selfish Gene&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Richard Dawkins&lt;/strong&gt; opened my eyes to the shocking reality that we are merely a carrier of our genes. So much of our lives, desires, and what makes us happy can be explained as proxies to further the gene’s agenda. Next time you see an attractive person on the internet or the street and feel that arousing sensation, you are just doing what your genes want you to do.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2uyXB8r&quot;&gt;&lt;strong&gt;The Nature Fix: Why Nature Makes Us Happier, Healthier, and More Creative&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Florence Williams&lt;/strong&gt; tells us to live close to nature to be happier. If you live in places without trees, do try to fill your surroundings with live plants. Hospitals with plants apparently heal people faster. If all the building managers read this book, we will all probably be happier and healthier.&lt;/p&gt;
&lt;h3&gt;Behavioral Economics&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-non-fiction-books/best-decade-non-fiction-books-202001151oXKWrcYYL.jpg&quot; alt=&quot;best-decade-non-fiction-books-202001151oXKWrcYYL.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2RT3cON&quot;&gt;&lt;strong&gt;Thinking, Fast and Slow&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Daniel Kahneman&lt;/strong&gt; gave birth to the behavioral economics field. This is a must-read for everyone interested in how we make decisions and how we think. Why does 1+1 feel effortless while 36 x 7 feels effortful? It is because those are processed by the two different systems in your brain.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2t5i7gI&quot;&gt;&lt;strong&gt;Predictably Irrational: The Hidden Forces That Shape Our Decisions&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Dan Ariely&lt;/strong&gt; cemented the concept that we are all predictably irrational. We probably know that we are sometimes irrational, but probably doesn’t yet understand that the irrationality can be predicted to a high degree of accuracy.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2sYK1e3&quot;&gt;&lt;strong&gt;Scarcity: Why Having Too Little Means So Much&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Sendhil Mullainathan&lt;/strong&gt; shows that life is all about scarcity. You will look at debt, poverty, and public policy differently after reading this book.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2O2rm8z&quot;&gt;&lt;strong&gt;Hooked: How to Build Habit-Forming Products&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Nir Eyal&lt;/strong&gt; revealed the strategy and techniques that companies use to hook people on their product. There are armies of people trying to get you hooked on their app or games right now, understanding the strategy would go a long way to defend against them.&lt;/p&gt;
&lt;h3&gt;Psychology &amp;amp; Self-Help&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-non-fiction-books/best-decade-non-fiction-books-202001171tET8K-20yL.jpg&quot; alt=&quot;best-decade-non-fiction-books-202001171tET8K-20yL.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2uF3ocv&quot;&gt;&lt;strong&gt;Range: Why Generalists Triumph in a Specialized World&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by David Epstein&lt;/strong&gt; is probably my pick of the decade. This book provides a counterpoint to the plethora of other literature preaching the doctrine of specialization, such as &lt;a href=&quot;https://amzn.to/36sLQxM&quot;&gt;&lt;strong&gt;Outliers&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Malcolm Gladwell&lt;/strong&gt;. The central thesis is that deliberate practice works really well for things that have unchanging rules e.g., playing the violin, but not optimal for inventing new things or finding connections between diverse domains of expertise. Read this. Love it or hate it, you won’t regret it. I’ve gifted this book to numerous people and always get a great review.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2O1xm1w&quot;&gt;&lt;strong&gt;Peak: Secrets from the New Science of Expertise&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by K. Anders Ericsson&lt;/strong&gt; and &lt;a href=&quot;https://amzn.to/2TWDMCy&quot;&gt;&lt;strong&gt;Grit&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Angela Duckworth&lt;/strong&gt; is very similar to &lt;a href=&quot;https://amzn.to/36sLQxM&quot;&gt;&lt;strong&gt;Outliers&lt;/strong&gt;&lt;/a&gt; mentioned above. They provide the view that specialization and perseverance is the key to the success of top performers. As I said above, this observation rings true for a lot of domains, but you might want to pair these books with &lt;a href=&quot;https://amzn.to/2uF3ocv&quot;&gt;&lt;strong&gt;Range&lt;/strong&gt;&lt;/a&gt; to get a more complete understanding.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/3aDWGnI&quot;&gt;&lt;strong&gt;The Millionaire Fastlane: Crack the Code to Wealth and Live Rich for a Lifetime!&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by M.J. DeMarco&lt;/strong&gt; provides a no-nonsense guide to getting rich. I like it because it doesn’t try to sell you new products or any pseudo-science. The fast lane really is just to find a way to increase your income significantly, then invest the money for early retirement.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/38NxQQN&quot;&gt;&lt;strong&gt;Principles: Life and Work&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Ray Dalio&lt;/strong&gt; will show you the framework in which Bridgewater Associates and Ray Dalio himself make decisions, prioritize and solve problems. You might have a completely different life principle from him, but learning the framework will definitely be beneficial for your life.&lt;/p&gt;
&lt;h3&gt;Economics&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/best-decade-non-fiction-books/best-decade-non-fiction-books-2020011316s7LYDYrL.jpg&quot; alt=&quot;best-decade-non-fiction-books-2020011316s7LYDYrL.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/3aDWYuO&quot;&gt;&lt;strong&gt;Debt: The First 5,000 Years&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by David Graeber&lt;/strong&gt; will teach you everything you need to know about the history of economics and our financial system. Mind you that this book is a tome, but it is worth it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/3aKPkyK&quot;&gt;&lt;strong&gt;Antifragile: Things That Gain from Disorder&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Nassim Nicholas Taleb&lt;/strong&gt; revealed the nature of self-healing systems that benefits from shocks and damage but only up to a point. This is unlike robustness. Human muscle is antifragile because it gets stronger with micro-injuries from resistance training. The stock market is also antifragile, but government intervention can actually cause more damage. Read more to learn why.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2uxS5D2&quot;&gt;&lt;strong&gt;Who Gets What — and Why: The New Economics of Matchmaking and Market Design&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;by Alvin E. Roth&lt;/strong&gt; goes deep into market and matchmaking. You will learn a lot of lessons about the market and economy in general.&lt;/p&gt;
&lt;p&gt;If you have any book recommendations for me, please let me know!&lt;/p&gt;
</content:encoded></item><item><title>I read 523 books last decade. Here are some fun stats</title><link>https://www.augusteo.com/blog/523-books/</link><guid isPermaLink="true">https://www.augusteo.com/blog/523-books/</guid><description>I read 523 books last decade. Here are some fun stats - a blog post by Victor Augusteo</description><pubDate>Sat, 04 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/523-books/523-books-202001blur-book-stack-books-bookshelves-590493.jpg&quot; alt=&quot;523-books-202001blur-book-stack-books-bookshelves-590493.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Photo by Janko Ferlic&lt;/p&gt;
&lt;p&gt;I started tracking my book reading on &lt;a href=&quot;https://www.goodreads.com/user/show/4577855-victor&quot;&gt;Goodreads&lt;/a&gt; around 2009. Since then, I’ve read an average of 52 books per year.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/523-books/523-books-202001ScreenShot2020-01-04at11.19.42PM.jpg&quot; alt=&quot;523-books-202001ScreenShot2020-01-04at11.19.42PM.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;One of my resolution in 2012 was to read more books, and I somehow was able to keep it up until now. I’ve no idea how.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/523-books/523-books-202001ScreenShot2020-01-04at11.16.04PM.jpg&quot; alt=&quot;523-books-202001ScreenShot2020-01-04at11.16.04PM.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Drilling more in-depth into the months, you can see that each month was different. The most were Oct 2012 and March 2018, in which I managed to read 17 books. Some months were either one, or no book read at all.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What does it all mean?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In my experience, you should just read when you can. There will be times when you are too busy or too into this game or that tv show, and that’s fine. Some breaks are good to recharge the fun of reading.&lt;/p&gt;
&lt;p&gt;If you don’t like reading, that’s fine too. Don’t let anyone pressure you into doing something you don’t enjoy. However, you might just haven’t found your kind of books that will keep you coming back.&lt;/p&gt;
&lt;p&gt;These charts are generated by Goodreads &lt;em&gt;reading stats&lt;/em&gt; tool.&lt;/p&gt;
</content:encoded></item><item><title>21 Lessons from ’Apollo: Race to the Moon’</title><link>https://www.augusteo.com/blog/apollo-book/</link><guid isPermaLink="true">https://www.augusteo.com/blog/apollo-book/</guid><description>21 Lessons from ’Apollo: Race to the Moon’ - a blog post by Victor Augusteo</description><pubDate>Sun, 04 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/apollo-book/apollo-book-282086.jpg&quot; alt=&quot;apollo-book-282086.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;by Charles Murray &amp;amp; Catherine Bly Cox&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;My rating: 5⁄5&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This year is the 50th anniversary of the moon landing, so our book club at Apple voted to read this book. I love this book. It tells the story of the entirety of the Apollo program, from inception to completion. A lot can be learned from this fantastic tale.&lt;/p&gt;
&lt;p&gt;Quoted texts are taken from the book. Emphasis mine.&lt;/p&gt;
&lt;h3&gt;1. Russians can be very cheeky&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;The United States … was working on a grapefruit-sized satellite weighing three and a half pounds. … launch this puny competitor to Sputnik in front of television and newspaper cameras from around the world. The Vanguard rocket being used for the launch rose four feet into the air, fell back, and crumpled onto the pad in a spectacular explosion. A few days later, the Soviet delegate to the United Nations inquired solicitously whether the United States was &lt;strong&gt;interested in receiving aid earmarked for underdeveloped countries&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;2. Keep it simple&lt;/h3&gt;
&lt;p&gt;a.k.a. &lt;a href=&quot;https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it&quot;&gt;YAGNI principle&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the early day of NASA, they have a limited budget and haven’t built a sophisticated transport system for the spacecraft. They just lined up an old pickup truck with mattresses and got the job done. Too bad I couldn’t find the picture.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pictures still in the files attest to the story, showing a battered pickup with a spacecraft on mattresses pulling up to the pad where an Atlas rocket stands waiting.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;3. Building a construction marvel is easier than conveying its scale to the general public&lt;/h3&gt;
&lt;p&gt;To house the gigantic Saturn V rocket, NASA built a &lt;a href=&quot;https://en.wikipedia.org/wiki/Vehicle_Assembly_Building&quot;&gt;mega-warehouse&lt;/a&gt; that’s really massive in scale. The construction went fine, but they have trouble taking a photo to show the size.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/apollo-book/apollo-book-vab_exterior_and_lcc.jpg&quot; alt=&quot;apollo-book-vab_exterior_and_lcc.jpg&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Yet conveying this size in ways other than numbers turns out to be impossible. Ray Clark, who ran the Cape’s support facilities in the 1960s, recalled that as the V.A.B. was nearing completion, NASA wanted to show the public how big it really was. But nothing their photographers produced captured the immensity. NASA got in touch with Life magazine, whose editors replied loftily that NASA need not worry, Life’s photographers knew how to do that sort of thing. The Life photographers flew down and set up shooting positions from every conceivable angle. They shot hundreds of rolls of film and went back to their labs in New York. “A while later they came back to see us,” Clark remembered. “They said, ‘We don’t know how to do it either. There’s just no way to portray photographically, or any way we know of, what the building’s really like.’”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;4. Planning for the impossible makes it easy to imagine building the extremely hard&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/apollo-book/nova_rocket-1--52e226ae.jpg&quot; alt=&quot;nova rocket 1  52e226ae&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If they had not been talking about the Nova, the huge Saturn would have seemed impossible. “The fact that we could postulate Nova with somewhat of a straight face automatically made the five-eighths-size Saturn credible.” It is one of the tricks engineers sometimes use on themselves, Maynard said, to maneuver themselves into taking on tasks that would otherwise be terrifying.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;5. Rocket science is more uncomplicated than car&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Liquid fuel is pumped into a combustion chamber in the presence of liquid oxygen and a flame. It burns. That’s all there is to it. There are no crankshafts to turn, no pistons to drive. The burning fuel produces energy in the form of gases that exit through the rocket’s nozzle. The force the gases produce against the top of the engine is called thrust. The thrust is transmitted through the rocket’s structure and, if it is greater than the weight of the rocket, the rocket lifts off. Put in its most basic terms, for any rocket to work there are two things that must be done extremely well: The propellants must be brought together, and then they must burn smoothly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;6. Exploding a bomb inside of the engine is a totally valid way to test the engine’s stability&lt;/h3&gt;
&lt;p&gt;The engineers were having a problem reproducing an engine instability problem. The problem arises seemingly in random. In software engineering, we call this &lt;a href=&quot;https://en.wikipedia.org/wiki/Heisenbug&quot;&gt;Heisenbug&lt;/a&gt; . So how would they reproduce the issue consistently? To plant a bomb inside of it, of course.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;… for testing purposes they must “be able to initiate this instability at our command,” as Castenholz put it. Nature, left to herself, wasn’t producing instabilities frequently enough for the team to learn how to control them. Therefore, they decided to explode a bomb in the combustion chamber while the test was in progress—if that didn’t drive the engine into instability, then nothing would, they reasoned. Then they could concentrate on making the engine damp out the instabilities they had induced.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After numerous bomb explosions and iteration, they got a system that would mend itself.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;They fiddled and nudged, and the incidence of instability decreased still further until, by late 1964, they weren’t getting it anymore. The bombs would explode, the pressure in the combustion chamber would skyrocket—and then the engine would be running smoothly again, not just within the 400-millisecond goal the team had set for itself, but within 100 milliseconds.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;7. The best way for higher up to know what’s going on is to communicate directly with the engineers&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Joseph_Francis_Shea&quot;&gt;Joe Shea (Deputy Director of NASA)&lt;/a&gt; would get each engineering team to provide a weekly report. He would read and annotate it. This is the way they come out with to let the higher-ups stay in touch with the ground-level work.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Markley went away to produce the first edition of the loose-leaf notebook that he and his staff would prepare for Shea every week for the next 165 weeks. Each notebook ran to more than a hundred pages and was on Shea’s desk by close of business Thursday. Shea would get up at 4 A.M. Friday and start to annotate it, usually with technical comments and instructions, occasionally with remarks such as “You’ve gotta be kidding.” He would work on the notebook intermittently from Friday morning through the weekend and return it to Markley on Monday morning. By Monday afternoon, everyone in the ASPO network would have gotten his response from Shea. With the annotated notebook in his hands, Markley became the action officer. “I’d take this notebook, and then I’d fan it back out very quickly and say, ‘Hey, we’ve got these questions and I want the answers in the notebook by this Thursday.’ We’d get the answers back and then we’d take Joe’s marked-up notebook and start off in the front, anything he had marks on. We’d have the answers there. And then we’d have the brand-new section with the newest status too.” The scrawled notations to and fro were a way of keeping up “a running communication,” in Shea’s words, without having to type up long memos. “Even if I didn’t see [the project officers] directly, I’d at least been in touch with them, and they’d been back in touch with me.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;8. “If you understand it, you can make me understand it.”&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;And then Joe would begin to grill you. There was no point in trying to sidestep or fudge an answer. It made no difference if Shea wasn’t a specialist in your area. “&lt;strong&gt;If you understand it, you can make me understand it&lt;/strong&gt;,” Shea kept saying, and you had better understand it absolutely cold or Shea would dissect you.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;9. Keep it sweet and simple&lt;/h3&gt;
&lt;p&gt;a.k.a avoid over-engineering.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;the people who wrote the specifications for the fuel gauges had reasoned that, because it was so important, they needed a highly accurate fuel gauge system. The way they would do it was to build a Geiger-counter arrangement that would infer the bulk of fuel left in the tank by the attenuation of radiation sources that would be transmitted through the fuel. It was a challenging bit of technology, no doubt about it—so challenging that the gauge they had developed so far was accurate only to about 10 percent and it kept failing quality tests for reliability. Shea let this process run for a couple of months and then stepped in. “This thing doesn’t make any sense to me,” he said. “We don’t need anything that complicated.” Shea had a Karmann Ghia that didn’t even need a fuel gauge, because it had a little reserve fuel tank that he could switch to. And that’s what they ended up doing for the R.C.S. fuel tanks. They had a big tank that they used during the mission, and a little tank, not to be touched unless the big tank ran out, but that could get the crew home safely. No more nuclear gas gauge.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;10 .“The better is the enemy of the good”&lt;/h3&gt;
&lt;p&gt;This discussion comes up surprisingly often in tech. Should we rewrite our software in this newer, sexier framework/language? The answer is usually no because if you had time to ask this question, often you would already have a satisfactory working product.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Because if we fail a qual test and if I have to send it back to redesign,” Shea explained, “then I have a less mature product when it comes back than I had already gotten up to that point.” The better is the enemy of the good.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;11. Trust in reducible complexity&lt;/h3&gt;
&lt;p&gt;The system can be complicated, but you can tackle it by understanding each of the pieces.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Hey, it isn’t that complicated. It is very understandable. The engines work this way, the guidance system works that way, the transistors work this way, so don’t get yourself in a state of mind thinking that it’s too complex. &lt;strong&gt;It really is very simple. It’s piece by piece&lt;/strong&gt;.” It’s awfully big, that’s all, Shea kept saying. Piece by piece, it’s simple.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;12. Even during the busy time, we need to have a calmer period to reflect&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/apollo-book/apollo_1_-E2-80-93_exterior_view_of_spacecraft_aft-f5f2db10.jpg&quot; alt=&quot;apollo 1  E2 80 93 exterior view of spacecraft aft f5f2db10&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The most significant accident of the Apollo program happened during the &lt;a href=&quot;https://en.wikipedia.org/wiki/Apollo_1#Fire&quot;&gt;test launch of Apollo 1&lt;/a&gt;. A catastrophe where all the crew were killed. The grief gave everyone time to pause and reflect.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“once the fire occurred, the flight schedule came to a screeching halt and everybody stopped and took stock.” People then had the time to go back and work in a less pressured fashion on “all of these things that everyone had in their back pocket that they should have worked on, and hadn’t had a chance to.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;13. “If you don’t know what to do, don’t do anything.”&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;“&lt;strong&gt;If you don’t know what to do, don’t do anything&lt;/strong&gt;.” But it was simple as a koan is simple, for flight controllers were trained and conditioned to solve problems; the temptation was for a controller to think that he knew what to do when he really didn’t. True wisdom in flight control lay in being able to recognize one’s own ignorance. Much of the training of the flight controllers consisted of showing them how they could be fooled.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;14. Hire smart people &amp;amp; trust them to make a right decision&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Flight had to listen carefully to every word and nuance of the answers. “&lt;strong&gt;The most likely error we can make in the business is not listening&lt;/strong&gt;,” said Kranz. “We’ve got very smart people [in the controllers]. We breed them to be very smart, we breed them to give opinions. We breed them to work in an arena of conflict. Sometimes they may disagree with you. Sometimes they may not be too smooth in words. Sometimes they may come in on an untimely basis and disrupt your train of thought.” But none of that can interfere with your listening and understanding precisely what it is that you are hearing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Then another during the Apollo 13 accident.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For Glynn Lunney, standing alongside Kraft, the reaction of the men in the viewing room was inspiring. The option Kraft was recommending had the obvious disadvantage of leaving the crew at risk for twenty-four hours longer than the alternatives, but there was no quibbling. Lunney: “These men who we all grew up thinking were kings, absolute tops in our business, sat in this room and said to us, ‘Look, we’re here to support you guys, and we just want to hear what’s going on and we want you to know that if there’s anything at all that you need, you just let us know.’” Then and in the days to come, Lunney recalled, “&lt;strong&gt;they never second-guessed, never reversed anything, never even came close to that. No cover-your-ass&lt;/strong&gt;. The whole set of interactions was one of the most supportive and reassuring you can imagine.” The gathering quickly ratified Kraft’s choice: The Apollo 13 crew would come home more slowly, but, they hoped, more surely.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;15. Don’t mess with the rocket fuel tank&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;One day, a technician at White Sands working with a fully fueled test article was filling out a report and absentmindedly clicked the end of his ballpoint pen on a fuel tank. The pen exerted just enough extra pressure on the tank to open a pinprick leak. Eventually, or so the story goes, they found the pen embedded in a fence post, along with a nub of finger bone. The wire-thin stream of propellant had sliced through flesh and bone as neatly as a scalpel.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;16. “The Eagle has landed”&lt;/h3&gt;
&lt;p&gt;When Neil Armstrong and Buzz Aldrin landed on the moon, they announced like so:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Houston, Tranquility base here,” Neil Armstrong announced. “The Eagle has landed.” “Roger, Tranquility, we copy you on the ground. You’ve got a bunch of guys about to turn blue. We’re breathing again. Thanks a lot.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;17. Anything that could go wrong will go wrong, so always be prepared&lt;/h3&gt;
&lt;p&gt;Apollo 13 accident was so unlikely that they never even practised it in the simulator.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Such experiences led to a few procedures that could be pulled off the shelf during Apollo 13. Still, the flight control team had never developed procedures for an accident of the magnitude of Thirteen’s. They had imagined losing one of the two oxygen tanks, or a battery, or even the S.P.S. engine. But never had the simulations postulated that the C.S.M. would be completely dead. Surely, everything thought, anything that knocked out both oxygen tanks would also destroy the spacecraft. Apollo 13 brought &lt;strong&gt;a change in simulation policy.&lt;/strong&gt; FIDO Dave Reed summarized the new ground rule: “&lt;strong&gt;They can throw anything at us they want and we won’t object.&lt;/strong&gt;”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;18. In unusual circumstances, forget the rulebook and improvise&lt;/h3&gt;
&lt;p&gt;When you encounter something that has never happened before, it might mean that you will have to do something you’ve never done before.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To conserve water and power, the engineers in the MER went systematically through the spacecraft, looking at every component to see whether it could be turned off completely. If not, they calculated the lowest voltage at which any given piece of equipment would operate and the minimal configuration in which a system would continue to perform the functions it absolutely had to perform. Forget the specs, forget the rulebooks, forget the operations manuals, Arabian announced. &lt;strong&gt;Go back to the physics of the design that no one knows better than you, the knowledge that’s not written down anywhere&lt;/strong&gt;, and decide what’s the least we can really get away with.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;19. Rely on checklists&lt;/h3&gt;
&lt;p&gt;NASA has &lt;a href=&quot;https://blog.nuclino.com/the-simple-genius-of-checklists-from-b-17-to-the-apollo-missions&quot;&gt;checklists for everything&lt;/a&gt; . When the Apollo 13 accident happened, they needed the astronaut to solve some problem, and of course, they created a checklist for them too.&lt;/p&gt;
&lt;p&gt;The checklist system is also &lt;a href=&quot;https://www.goodreads.com/book/show/6667514-the-checklist-manifesto&quot;&gt;widely used in medicine&lt;/a&gt; .&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So as the White Team assembled Wednesday morning, they were a disheveled and weary crew, with a little more than two days left before splashdown. Under ordinary circumstances, preparing an entry checklist for a lunar mission was a three-month job.&lt;/p&gt;
&lt;p&gt;The creation of the checklist was a controlled frenzy in which solutions were developed, rejected, and replaced in such rapid-fire sequence that it was difficult at any one moment to be sure how far they had to go.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;20. Astronauts jobs are really, really hard&lt;/h3&gt;
&lt;p&gt;During the Apollo 13 accident, the astronaut had to survive on a meagre amount of water &amp;amp; sleep.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;By Thursday, the temperature in Odyssey was thirty-eight degrees, about that of a refrigerator. Until then, the crew had used Odyssey as the bedroom of a two-room apartment; now they huddled together in the little LEM, which was marginally warmer. But Aquarius, too, was uncomfortable. The walls and windows of the LEM were moist with condensed water. The men were averaging three hours of fretful sleep per day. Also, concerned about having enough water to cool the electronics, all three astronauts had cut their fluid intake to near zero. They deliberately ignored the Surgeon’s instructions to drink—since they didn’t feel that thirsty anyway (one doesn’t, in space), they chose to save water for the equipment. The result was that, without knowing it, all three were becoming dehydrated. Haise had developed a kidney infection and was running a fever. &lt;strong&gt;Fatigue. Cold. Dehydration&lt;/strong&gt;. As they neared time for entry, the normal performance levels of all three had deteriorated. Now they were going to have to assimilate a hastily written entry checklist, perform a crucial midcourse correction, and then bring the spacecraft home without any of the backup systems that ordinarily provided some margin for error.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;21. If you want to maintain a good family relationship, don’t work too much&lt;/h3&gt;
&lt;p&gt;Work-life balance is imperative, especially when you have spouse and kids.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In many cases, families didn’t survive the stress—during the mid-1960s, the towns around K.S.C. had the highest divorce rate in the country.&lt;/p&gt;
&lt;p&gt;The consensus is that this is no coincidence. Under the heat of Apollo, your marriage either melted or annealed.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>How to use NextDNS with AsusWRT-Merlin [Tutorial]</title><link>https://www.augusteo.com/blog/nextdns-asuswrt-merlin/</link><guid isPermaLink="true">https://www.augusteo.com/blog/nextdns-asuswrt-merlin/</guid><description>How to use NextDNS with AsusWRT-Merlin [Tutorial] - a blog post by Victor Augusteo</description><pubDate>Sat, 01 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Update 1/4/2020&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Olivier mentioned that this is now directly integrated with Merlin.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Updated 7/27/2019&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Recently I read about &lt;a href=&quot;https://www.nextdns.io/&quot;&gt;NextDNS&lt;/a&gt; from this &lt;a href=&quot;https://news.ycombinator.com/item?id=20012687&quot;&gt;Hackernews post&lt;/a&gt;. I used to run &lt;a href=&quot;https://pi-hole.net/&quot;&gt;Pi-hole&lt;/a&gt; to manage DNS-level ad-blocking but its quite a pain to setup. There’s always some issue going on with the Raspberry Pi itself and needed to spend time to manage it.&lt;/p&gt;
&lt;p&gt;Now with this service, I can have the DNS blocking like Pi-hole without the hassle!&lt;/p&gt;
&lt;p&gt;Setting it up with AsusWRT-Merlin is easy, but NextDNS doesn’t have a setup guide for it. So I decided to write one.&lt;/p&gt;
&lt;h3&gt;1. Setup new account on NextDNS&lt;/h3&gt;
&lt;p&gt;Follow this link to get started: &lt;a href=&quot;https://my.nextdns.io/start&quot;&gt;Create config&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Then go to &lt;strong&gt;Setup&lt;/strong&gt; panel and find the &lt;strong&gt;DNS-over-TLS&lt;/strong&gt; &amp;amp; &lt;strong&gt;DNS Servers&lt;/strong&gt; IP addresses.&lt;/p&gt;
&lt;p&gt;It should look like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nextdns-asuswrt-merlin/nextdns-asuswrt-merlin-screen_shot_2019-07-27_at_8_27_58_pm.jpg&quot; alt=&quot;nextdns-asuswrt-merlin-screen_shot_2019-07-27_at_8_27_58_pm.jpg&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2. Open DNS panel&lt;/h3&gt;
&lt;p&gt;You can get there by clicking &lt;strong&gt;WAN&lt;/strong&gt; button on the left menu.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nextdns-asuswrt-merlin/nextdns-asuswrt-merlin-screenshot-2019-06-01-11.36.01.jpg&quot; alt=&quot;nextdns-asuswrt-merlin-screenshot-2019-06-01-11.36.01.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Previously I used CloudFlare’s DNS, and it worked quite well. But now we want to switch to NextDNS!&lt;/p&gt;
&lt;h3&gt;3. Enter the NextDNS details&lt;/h3&gt;
&lt;p&gt;First, you want to delete the existing DNS Server 1 &amp;amp; 2.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/nextdns-asuswrt-merlin/nextdns-asuswrt-merlin-screen_shot_2019-07-27_at_8_31_25_pm.jpg&quot; alt=&quot;nextdns-asuswrt-merlin-screen_shot_2019-07-27_at_8_31_25_pm.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Then, just follow the config example above, then copy paste the &lt;strong&gt;DNS Servers to Addresses tab&lt;/strong&gt; and &lt;strong&gt;DNS-over-TLS to TLS Hostname&lt;/strong&gt;. The port is &lt;strong&gt;853&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;After that, you should reboot the router.&lt;/p&gt;
&lt;h3&gt;4. Done!&lt;/h3&gt;
&lt;p&gt;Now your whole network should be redirected to NextDNS filtering.&lt;/p&gt;
&lt;p&gt;You can start enabling blocker lists like EasyList and StevenBlack from the List panel on NextDNS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Edit 7⁄27: The below section is now outdated. They routed me to a server that’s very close to my area :)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Not all great… yet.&lt;/p&gt;
&lt;p&gt;The service works wonderfully considering that this is so new and still in beta. Right now I’m being redirected to Miami VPS instead of the one in CA even though I’m in CA.&lt;/p&gt;
&lt;p&gt;You can check that by going to the &lt;a href=&quot;https://ipv4.dns.nextdns.io/ping&quot;&gt;ping router page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The founders are very responsive. I’ve chatted with them, and I’m delighted by their help.&lt;/p&gt;
</content:encoded></item><item><title>That one time I visited Rotary club of Southbank</title><link>https://www.augusteo.com/blog/rotary-southbank/</link><guid isPermaLink="true">https://www.augusteo.com/blog/rotary-southbank/</guid><description>That one time I visited Rotary club of Southbank - a blog post by Victor Augusteo</description><pubDate>Fri, 15 Mar 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I am currently visiting Melbourne to activate my H1b visa.&lt;/p&gt;
&lt;p&gt;Spent an evening with the Southbank Rotary club. Great people.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.southbank.org.au/blog/volunteers-are-like-angels-on-earth&quot;&gt;https://www.southbank.org.au/blog/volunteers-are-like-angels-on-earth&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/rotary-southbank/rotary-southbank-fullsizerender.jpeg&quot; alt=&quot;rotary-southbank-fullsizerender.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/rotary-southbank/rotary-southbank-fullsizerender-2.jpeg&quot; alt=&quot;rotary-southbank-fullsizerender-2.jpeg&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>That one time I taught high school students web dev</title><link>https://www.augusteo.com/blog/highschool-students-web-dev/</link><guid isPermaLink="true">https://www.augusteo.com/blog/highschool-students-web-dev/</guid><description>That one time I taught high school students web dev - a blog post by Victor Augusteo</description><pubDate>Tue, 26 Feb 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last month I taught students at Monta Vista High School (MVHS) on basics of web dev.&lt;/p&gt;
&lt;p&gt;Full article here: &lt;a href=&quot;https://elestoque.org/2019/01/22/briefs/mvhs-future-business-leaders-of-americas-kicks-off-technology-program/&quot;&gt;https://elestoque.org/2019/01/22/briefs/mvhs-future-business-leaders-of-americas-kicks-off-technology-program/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/highschool-students-web-dev/highschool-students-web-dev-dsc_0843-copy.jpg&quot; alt=&quot;highschool-students-web-dev-dsc_0843-copy.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Memorable quote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“If you don’t know it, Google it,” Augusteo said. “Even engineers Google everything. Welcome to web development.”&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>Hackathon Resources</title><link>https://www.augusteo.com/blog/hackathon-resources/</link><guid isPermaLink="true">https://www.augusteo.com/blog/hackathon-resources/</guid><description>Hackathon Resources - a blog post by Victor Augusteo</description><pubDate>Sat, 19 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This article is a companion to a tech talk I’m giving to Monta Vista Highschool Entrehack event.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.icloud.com/keynote/0_dTyQ0TkhTe-KxGGFXhJLHzw#MontaVistaHackathon_techtalk&quot;&gt;Click here&lt;/a&gt; for the Keynote presentation, and be sure to click ‘show presenter note’ on top left for my notes.&lt;/p&gt;
&lt;p&gt;These links are a rough guide to help you get started with your hackathon project. Use them wisely.&lt;/p&gt;
&lt;p&gt;The key is to focus on the vision and fleshing out your ideas during the hackathon and not to spend time dealing with app architecture and boilerplates.&lt;/p&gt;
&lt;p&gt;Read this if your goal is to win. Otherwise, you can skip to the next section.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/garyyauchan/ultimate-8-step-guide-to-winning-hackathons-84c9dacbe8e&quot;&gt;Ultimate 8 Step Guide to Winning Hackathons – garyyauchan – Medium&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Tech links&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Prototyping&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Sometimes you could do really well with just prototyping the user experience without doing any coding. &lt;a href=&quot;https://www.invisionapp.com/&quot;&gt;Invision&lt;/a&gt; is a really good service to try this. If you prefer a native Mac app, I highly recommend &lt;a href=&quot;https://www.flinto.com/&quot;&gt;Flinto&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Web Builder&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you only need simple websites and prefer drag and drop, &lt;a href=&quot;https://www.wix.com/&quot;&gt;Wix.com&lt;/a&gt; &amp;amp; &lt;a href=&quot;https://webflow.com/&quot;&gt;Webflow&lt;/a&gt; works really well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Infrastructure&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://firebase.google.com/&quot;&gt;Firebase&lt;/a&gt; is the easiest to get started if you need to store data like user accounts, pictures, database, etc. For more complex infrastructure, use &lt;a href=&quot;https://aws.amazon.com/&quot;&gt;Amazon Web Services (AWS)&lt;/a&gt; or &lt;a href=&quot;https://azure.microsoft.com/en-us/&quot;&gt;MS Azure&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Web Boilerplates&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/sahat/hackathon-starter&quot;&gt;This project&lt;/a&gt; is a very popular web boilerplate that has a lot of services baked in. Easy to get started too. It also have a bunch of recommended libraries/links for you to use.&lt;/p&gt;
&lt;p&gt;Some other boilerplates if you prefer &lt;a href=&quot;https://www.reactboilerplate.com/&quot;&gt;React.js&lt;/a&gt; or &lt;a href=&quot;http://seed.happyfuncorp.com/&quot;&gt;Rails&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cross Platform (Electron)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you really need to build apps for all platform, you should use &lt;a href=&quot;https://github.com/electron/electron-quick-start&quot;&gt;Electron&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;iOS&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;iOS is pretty easy to get started, but &lt;a href=&quot;https://github.com/vsouza/awesome-ios&quot;&gt;this awesome page&lt;/a&gt; helps to find projects that you can reuse.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explore&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/&quot;&gt;GitHub&lt;/a&gt; is the most popular source control website and you should use it to search for projects to clone or to learn from.&lt;/p&gt;
&lt;p&gt;You should also checkout this &lt;a href=&quot;https://github.com/sindresorhus/awesome&quot;&gt;mother of awesome lists&lt;/a&gt; to find out other awesome lists.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Machine Learning&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Adding machine learning capabilities are actually really easy with these pre-trained models and services. Use &lt;a href=&quot;https://github.com/likedan/Awesome-CoreML-Models&quot;&gt;CoreML Models&lt;/a&gt; if you are building for iOS. You can also try &lt;a href=&quot;https://aws.amazon.com/rekognition/?nc2=h_m1&quot;&gt;Amazon Rekognition&lt;/a&gt; or other ML services for web and mobile apps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SMS&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you want to play around with SMS sending/receiving, you should try &lt;a href=&quot;https://www.twilio.com/&quot;&gt;Twilio&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hit me up if you have any questions.&lt;/p&gt;
</content:encoded></item><item><title>My Favourite Books of 2018</title><link>https://www.augusteo.com/blog/fav-books-2018/</link><guid isPermaLink="true">https://www.augusteo.com/blog/fav-books-2018/</guid><description>My Favourite Books of 2018 - a blog post by Victor Augusteo</description><pubDate>Sun, 23 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This year I &lt;a href=&quot;https://www.goodreads.com/user_challenges/10155201&quot;&gt;read eighty books&lt;/a&gt; which is my highest record. I might have to update &lt;a href=&quot;https://hugo.augusteo.com/blog/how-to-read-62-books-in-a-year/&quot;&gt;my old how-to article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We will start with fictions, then move on to non-fiction.&lt;/p&gt;
&lt;h2&gt;Fiction&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2t0aCYd&quot;&gt;The Wise Man’s Fear by Patrick Rothfuss&lt;/a&gt; is hands down one of the best fantasy tales I’ve ever read in my life. Rothfuss managed to outdo the magic of his first book &lt;a href=&quot;https://amzn.to/2RQQ64S&quot;&gt;The Name of the Wind&lt;/a&gt; and made it even grander. If you read just one book from my recommendation, please let it be this one.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2TYGMhT&quot;&gt;The Dark Forest by Liu Cixin&lt;/a&gt; and its prequel &lt;a href=&quot;https://amzn.to/3aUZmh4&quot;&gt;The Three-Body Problem&lt;/a&gt; are fantastic sci-fi that ponders the question of what would humanity do on the face of an invasion of a superior alien civilization? This is probably one of the few Chinese works that become very popular in the west.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2RNgMDu&quot;&gt;Oathbringer (The Stormlight Archive, #3) by Brandon Sanderson&lt;/a&gt; has left me wanting more of the classic Sanderson tale. You must read the previous books to understand the story. I love the magic system in this series, it just makes so much sense.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/38JRkFA&quot;&gt;The Underground Railroad by Colson Whitehead&lt;/a&gt; showed me how terrible life under slavery could be and how people would fight systematic oppression. I didn’t know that the &lt;a href=&quot;https://en.wikipedia.org/wiki/Underground_Railroad&quot;&gt;Underground Railroad&lt;/a&gt; isn’t a real railroad as it was depicted in the book.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/38KQMzE&quot;&gt;The Ocean at the End of the Lane by Neil Gaiman&lt;/a&gt; this book went from zero to eleven really fast. What started as a slow-paced nostalgic story just changed gear into fantasy horror so suddenly, yet with a very believable transition.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/30W9fX2&quot;&gt;Artemis by Andy Weir&lt;/a&gt; tells the story of a moon-base in the future. I love how Weir painted the details of the tech and how our future could be.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2U5KUMW&quot;&gt;Pachinko by Min Jin Lee&lt;/a&gt; is the first book I’ve read about the Asian side of the second world war. Be warned that this book is a multi-generational epic and you might want to sketch the names of the characters to keep track of their relations.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/30WPOgN&quot;&gt;Mythos: The Greek Myths Retold by Stephen Fry&lt;/a&gt; and &lt;a href=&quot;https://amzn.to/3aKNlut&quot;&gt;Norse Mythology by Neil Gaiman&lt;/a&gt; are both excellent modern retelling of the classic mythology stories.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/310OlpJ&quot;&gt;Habibi by Craig Thompson&lt;/a&gt; is an exceptional story from the middle east in the form of graphic novel.&lt;/p&gt;
&lt;h2&gt;Non-Fiction&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/30UMOS0&quot;&gt;Being Mortal: Medicine and What Matters in the End by Atul Gawande&lt;/a&gt; discusses the end-of-life treatment in modern medicine. What happens when there is no hope of recovery? What happens when the pain of chemotherapy get so high that it is better just to give up? At what point do we trade quality of life with the risk of treatments? I think we as a society aren’t paying enough attention to this topic and this book should be a must-read for everyone.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2GqkpKj&quot;&gt;Extreme Ownership: How U.S. Navy SEALs Lead and Win by Jocko Willink&lt;/a&gt; and its sequel &lt;a href=&quot;https://amzn.to/3aPXfLx&quot;&gt;The Dichotomy of Leadership&lt;/a&gt; posits that a leader’s primary role is to take ownership of the mission. I enjoyed the format of the book wherein every chapter Willink and Babin tell their real combat story which was then followed by its learning and application in the business world. Highly recommended.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2t0gbWJ&quot;&gt;Surely You’re Joking, Mr. Feynman!: Adventures of a Curious Character by Richard Feynman&lt;/a&gt; is a fantastic biography of a genius who just wants to have fun. Feynman makes almost everything he does look easy and enjoyable.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/36uWpjM&quot;&gt;An Astronaut’s Guide to Life on Earth by Chris Hadfield&lt;/a&gt; follows the commander’s story from his childhood to retirement. You will get a deep insight into the life of an astronaut and its challenges.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2U62RLk&quot;&gt;Free to Be Ruth Bader Ginsburg: The Story of Women and Law by Teri Kanefield&lt;/a&gt; tells the real-life story of the US supreme court judge that is still active as of now. You will come to appreciate just how much circumstances line up to pave her path to the highest judicial office in the nation.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2RQOFDx&quot;&gt;Meditations by Marcus Aurelius&lt;/a&gt; is a classic that I would probably read once a year every year. The teaching of stoicism is really timeless and would apply to you where ever your current station in life might be.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2O1xm1w&quot;&gt;Peak: Secrets from the New Science of Expertise by K. Anders Ericsson&lt;/a&gt; divulge the secrets of world-class performers. To give you a spoiler, it is deliberate practice. This book could help you understand how Feynman, Hadfield and Ginsburg did what they do.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/30V69CG&quot;&gt;A Primate’s Memoir: A Neuroscientist’s Unconventional Life Among the Baboons by Robert M. Sapolsky&lt;/a&gt; tells the story of the biologist who lived and gets to know the baboons intimately. He wrote the stories so well that you could imagine yourself sitting there in the savannah with them! I really enjoyed it, and it made me appreciate how dedicated some researcher might be.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2tKiayS&quot;&gt;Enlightenment Now: The Case for Reason, Science, Humanism, and Progress by Steven Pinker&lt;/a&gt; says that despite all the bad stuff happening in the news every day, we are progressing really well. There are less illness, poverty, war &amp;amp; crimes in the world compared to the past. This is the best time to be alive!&lt;/p&gt;
</content:encoded></item><item><title>Drone photos from Cupertino and Apple Park Chapter Two</title><link>https://www.augusteo.com/blog/drone-cupertino-apple-park-two/</link><guid isPermaLink="true">https://www.augusteo.com/blog/drone-cupertino-apple-park-two/</guid><description>Drone photos from Cupertino and Apple Park Chapter Two - a blog post by Victor Augusteo</description><pubDate>Wed, 19 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park-two/DJI_0709-01def6d6.jpg&quot; alt=&quot;DJI 0709 01def6d6&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Second flight from Cupertino (Hamptons). This flight might be the last since there’s only so much you could shoot from here. Next Cupertino flight would probably be from Infinite Loop campus.&lt;/p&gt;
&lt;p&gt;I enjoyed experimenting with the top-down drone style shoots and trying out different elevation and angles. The dynamism is a significant improvement over the traditional camera - which was limited by your feet.&lt;/p&gt;
&lt;p&gt;However, I would love to have the dynamic range of SLR and better camera control on the drone. Even with five bracketed shot, you probably only get the same dynamic range as a single full-frame SLR RAW. I await the day that we can have a full-frame drone camera.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park-two/DJI_0630-1333f05b.jpg&quot; alt=&quot;DJI 0630 1333f05b&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park-two/DJI_0639-af729bd7.jpg&quot; alt=&quot;DJI 0639 af729bd7&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park-two/DJI_0650-b28e8f82.jpg&quot; alt=&quot;DJI 0650 b28e8f82&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park-two/DJI_0674-870f570d.jpg&quot; alt=&quot;DJI 0674 870f570d&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park-two/DJI_0693-79b6fdbf.jpg&quot; alt=&quot;DJI 0693 79b6fdbf&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park-two/DJI_0697-eea43699.jpg&quot; alt=&quot;DJI 0697 eea43699&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park-two/DJI_0709-01def6d6.jpg&quot; alt=&quot;DJI 0709 01def6d6&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park-two/DJI_0727-74ca9128.jpg&quot; alt=&quot;DJI 0727 74ca9128&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park-two/DJI_0730-6816dcd7.jpg&quot; alt=&quot;DJI 0730 6816dcd7&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park-two/DJI_0734-7c8af678.jpg&quot; alt=&quot;DJI 0734 7c8af678&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Hugo shortcode to display all images in a folder</title><link>https://www.augusteo.com/blog/hugo-shortcode-images/</link><guid isPermaLink="true">https://www.augusteo.com/blog/hugo-shortcode-images/</guid><description>Hugo shortcode to display all images in a folder - a blog post by Victor Augusteo</description><pubDate>Mon, 17 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;On posts that intents to share photos like &lt;a href=&quot;https://hugo.augusteo.com/blog/drone-photos-from-cupertino-and-apple-park/&quot;&gt;the drone post&lt;/a&gt;, I wanted to be able to include all the images from a single static folder into the post. Doing it manually is troublesome as I have to copy paste the filename one by one into markdown.&lt;/p&gt;
&lt;p&gt;There’s gotta be an easy way to do that, but I’ve no experience working with Hugo, so I asked in the forum: &lt;a href=&quot;https://discourse.gohugo.io/t/automating-inserting-markdown-of-all-images-in-a-subfolder/15764&quot;&gt;Automating inserting markdown of all images in a subfolder?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As you can see, people suggested using the &lt;a href=&quot;https://gohugo.io/content-management/page-resources/&quot;&gt;page resources&lt;/a&gt; which I don’t think works the way I wanted.&lt;/p&gt;
&lt;p&gt;My curiosity got the better of me and I learned how to write shortcode to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;accept a parameter which contain the folder name in &lt;code&gt;/static/img&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;loop through every file inside that folder&lt;/li&gt;
&lt;li&gt;display the images with some padding&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To use this shortcode, you need to create a file called &lt;code&gt;album.html&lt;/code&gt; in your theme’s shortcode directory and paste these code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- put bunch of photos in /static/img/albumName --&amp;gt;
&amp;lt;!-- and insert shortcode &quot;album albumName&quot; in blogpost.md --&amp;gt;
{{ $albumUrl := print &quot;/static/img/&quot; ($.Get 0) }}
{{ range readDir $albumUrl }}
    {{ $imgURL := print &quot;img/&quot; ($.Get 0) &quot;/&quot; .Name | absURL }}
    &amp;lt;p&amp;gt;&amp;lt;img src=&quot;{{ $imgURL }}&quot;/&amp;gt;&amp;lt;/p&amp;gt;
{{ end }}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can put all images that you wanted to post in a single folder, the just insert the shortcode in the post.&lt;/p&gt;
</content:encoded></item><item><title>Drone photos from Cupertino and Apple Park</title><link>https://www.augusteo.com/blog/drone-cupertino-apple-park/</link><guid isPermaLink="true">https://www.augusteo.com/blog/drone-cupertino-apple-park/</guid><description>Drone photos from Cupertino and Apple Park - a blog post by Victor Augusteo</description><pubDate>Thu, 13 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park/drone-cupertino-apple-park-DJI_0014.jpeg&quot; alt=&quot;drone-cupertino-apple-park-DJI_0014.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This flight was my first drone flight. My friend Alex was teaching me how to fly the drone and the camera options. It was fun but thrilling at the same time.&lt;/p&gt;
&lt;p&gt;We left work around 4 pm and walked south to get outside of the SJO airport range as legally required by the FAA. Funnily, Apple Park was just on the edge of the airport range, so we didn’t have to walk far.&lt;/p&gt;
&lt;p&gt;I took all the pictures with five bracketed shot, which were then processed together with AuroraHDR and Luminar. I am quite impressed that the drone was stable enough to take long exposure shots. Considering that they are floating in the air.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park/drone-cupertino-apple-park-DJI_0004.jpeg&quot; alt=&quot;drone-cupertino-apple-park-DJI_0004.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park/drone-cupertino-apple-park-DJI_0034.jpeg&quot; alt=&quot;drone-cupertino-apple-park-DJI_0034.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park/drone-cupertino-apple-park-DJI_0085.jpeg&quot; alt=&quot;drone-cupertino-apple-park-DJI_0085.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park/drone-cupertino-apple-park-DJI_0105.jpeg&quot; alt=&quot;drone-cupertino-apple-park-DJI_0105.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park/drone-cupertino-apple-park-DJI_0115.jpeg&quot; alt=&quot;drone-cupertino-apple-park-DJI_0115.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park/drone-cupertino-apple-park-DJI_0120_2.jpeg&quot; alt=&quot;drone-cupertino-apple-park-DJI_0120_2.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park/drone-cupertino-apple-park-DJI_0135_3.jpeg&quot; alt=&quot;drone-cupertino-apple-park-DJI_0135_3.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park/drone-cupertino-apple-park-DJI_0156.jpeg&quot; alt=&quot;drone-cupertino-apple-park-DJI_0156.jpeg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/drone-cupertino-apple-park/drone-cupertino-apple-park-DJI_0171.jpeg&quot; alt=&quot;drone-cupertino-apple-park-DJI_0171.jpeg&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Vallejo Marina - Drone Photos</title><link>https://www.augusteo.com/blog/vallejo-marina-drone/</link><guid isPermaLink="true">https://www.augusteo.com/blog/vallejo-marina-drone/</guid><description>Vallejo Marina - Drone Photos - a blog post by Victor Augusteo</description><pubDate>Tue, 11 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/vallejo-marina-drone/vallejo-marina-drone-DJI_0582.jpg&quot; alt=&quot;vallejo-marina-drone-DJI_0582.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I went to Vallejo yesterday because I saw an &lt;a href=&quot;https://www.reddit.com/r/bayarea/comments/a4i7kp/mare_island_strait_vallejo/&quot;&gt;interesting image on BayArea subreddit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Google maps shows that the location only an hour drive from my house and there’s a &lt;a href=&quot;https://goo.gl/maps/bSXjaa8Q4zN2&quot;&gt;big parking (municipal marina)&lt;/a&gt; and open space to launch drone from.&lt;/p&gt;
&lt;p&gt;The weather was frigid and windy. This flight is my third flight with the drone, and I’m still experimenting with how to fly the drone effectively.&lt;/p&gt;
&lt;p&gt;So far I love it because of the freedom to compose. Just maneuver your drone around until it looks good!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/vallejo-marina-drone/vallejo-marina-drone-DJI_0467.jpg&quot; alt=&quot;vallejo-marina-drone-DJI_0467.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/vallejo-marina-drone/vallejo-marina-drone-DJI_0480.jpg&quot; alt=&quot;vallejo-marina-drone-DJI_0480.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/vallejo-marina-drone/vallejo-marina-drone-DJI_0482.jpg&quot; alt=&quot;vallejo-marina-drone-DJI_0482.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/vallejo-marina-drone/vallejo-marina-drone-DJI_0490.jpg&quot; alt=&quot;vallejo-marina-drone-DJI_0490.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/vallejo-marina-drone/vallejo-marina-drone-DJI_0493.jpg&quot; alt=&quot;vallejo-marina-drone-DJI_0493.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/vallejo-marina-drone/vallejo-marina-drone-DJI_0507.jpg&quot; alt=&quot;vallejo-marina-drone-DJI_0507.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/vallejo-marina-drone/vallejo-marina-drone-DJI_0509.jpg&quot; alt=&quot;vallejo-marina-drone-DJI_0509.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/vallejo-marina-drone/vallejo-marina-drone-DJI_0513.jpg&quot; alt=&quot;vallejo-marina-drone-DJI_0513.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/vallejo-marina-drone/vallejo-marina-drone-DJI_0526.jpg&quot; alt=&quot;vallejo-marina-drone-DJI_0526.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/vallejo-marina-drone/vallejo-marina-drone-DJI_0543.jpg&quot; alt=&quot;vallejo-marina-drone-DJI_0543.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/vallejo-marina-drone/vallejo-marina-drone-DJI_0551.jpg&quot; alt=&quot;vallejo-marina-drone-DJI_0551.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/vallejo-marina-drone/vallejo-marina-drone-DJI_0566.jpg&quot; alt=&quot;vallejo-marina-drone-DJI_0566.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/vallejo-marina-drone/vallejo-marina-drone-DJI_0574.jpg&quot; alt=&quot;vallejo-marina-drone-DJI_0574.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/vallejo-marina-drone/vallejo-marina-drone-DJI_0594.jpg&quot; alt=&quot;vallejo-marina-drone-DJI_0594.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/vallejo-marina-drone/vallejo-marina-drone-DJI_0600.jpg&quot; alt=&quot;vallejo-marina-drone-DJI_0600.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/vallejo-marina-drone/vallejo-marina-drone-DJI_0608.jpg&quot; alt=&quot;vallejo-marina-drone-DJI_0608.jpg&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Why and How I Moved My Blog To Netlify</title><link>https://www.augusteo.com/blog/blog-netlify/</link><guid isPermaLink="true">https://www.augusteo.com/blog/blog-netlify/</guid><description>Why and How I Moved My Blog To Netlify - a blog post by Victor Augusteo</description><pubDate>Sun, 09 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/blog-netlify/blog-netlify-netlify.jpg&quot; alt=&quot;blog-netlify-netlify.jpg&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Why Netlify?&lt;/h2&gt;
&lt;p&gt;I have just recently moved my blog from Vultr to Netlify because I was lured by:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Free and easy to setup CI/CD. Just click one button and its all setup!&lt;/li&gt;
&lt;li&gt;Free (unlimited?) CDN hosting! They actually called it &lt;a href=&quot;https://www.netlify.com/features/adn/&quot;&gt;ADN&lt;/a&gt; with a nice graph to compare.&lt;/li&gt;
&lt;li&gt;Auto assets optimization like minifying asset files and lossless image compression.&lt;/li&gt;
&lt;li&gt;Form handling and Lambda functions integration. I haven’t need to use these yet, but the fact that they are available is exciting. Oh, the possibilities!&lt;/li&gt;
&lt;li&gt;If you use a static site generator, they offer &lt;a href=&quot;https://www.netlifycms.org/&quot;&gt;NetlifyCMS&lt;/a&gt; that work with generators like Jekyll, Hugo, Hexo, or Gatsby.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Steps to create and move&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Click on the ‘Deploy to Hugo’ button on &lt;a href=&quot;https://www.netlifycms.org/docs/start-with-a-template/&quot;&gt;this page&lt;/a&gt;. That will create a Netlify site and a repo on Github/Gitlab.&lt;/li&gt;
&lt;li&gt;Pull the repo to your machine.&lt;/li&gt;
&lt;li&gt;Move the &lt;code&gt;site/static/admin&lt;/code&gt; folder from Netlify CMS to a temporary folder.&lt;/li&gt;
&lt;li&gt;Replace the &lt;code&gt;site&lt;/code&gt; folder content with your old Hugo folder.&lt;/li&gt;
&lt;li&gt;Move back the &lt;code&gt;admin&lt;/code&gt; folder from the temp to &lt;code&gt;site/static&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Edit the &lt;code&gt;config.yml&lt;/code&gt; to fit your old Hugo structure.&lt;/li&gt;
&lt;li&gt;Commit and push.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Your site should be up now on url that looks like &lt;code&gt;some-random-word12314.netlify.com&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Use your own domain name&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;(optional) Rename the Netlify url to something good e.g. &lt;code&gt;augusteo.netlify.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Transfer your domain NameServer to Netlify’s. Then let Netlify set the A and CNAME DNS record.&lt;/li&gt;
&lt;li&gt;(optional) Change the MX and TXT record if you have mail server setup for your domain name.&lt;/li&gt;
&lt;li&gt;Set SSL cert - they are using Let’s encrypt and will get you a wildcard cert.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Netlify’s amazing customer service&lt;/h2&gt;
&lt;p&gt;I was having a problem where my site was still using Netlify’s wildcard cert instead of my own. When I emailed their support, I was expecting a slow reply. However, they replied in 5 minutes and fixed my problem!&lt;/p&gt;
&lt;p&gt;The quick response blew me away. This is better than some of the paid service! I highly recommend them based on this alone.&lt;/p&gt;
&lt;h2&gt;How can it be free? What’s the catch?&lt;/h2&gt;
&lt;p&gt;I asked them about it and how can they offer their services for free. Their answer:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hi Victor,&lt;/p&gt;
&lt;p&gt;The idea is that some users will use our paid services and we’ll have enough of those to cover the cost of people that don’t move to paid. We also count on people using our service to be &lt;strong&gt;evangelist and bring in others&lt;/strong&gt;. Then there are devs that use our free service on free plans and get their companies to move to our our paid plans. In addition to these self-serve, we offer custom enterprise contracts and push support and features and performance for them that you can’t otherwise get on free or self-serve paid.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;(emphasis mine).&lt;/p&gt;
&lt;p&gt;This post is my effort to evangelist Netlify in return for their amazing free service.&lt;/p&gt;
&lt;p&gt;You should give them a try.&lt;/p&gt;
</content:encoded></item><item><title>Is coding the still the best thing to teach to kids?</title><link>https://www.augusteo.com/blog/coding-best-kids/</link><guid isPermaLink="true">https://www.augusteo.com/blog/coding-best-kids/</guid><description>Is coding the still the best thing to teach to kids? - a blog post by Victor Augusteo</description><pubDate>Sat, 08 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/coding-best-kids/coding-best-kids-95125.jpg&quot; alt=&quot;coding-best-kids-95125.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I read this good article today:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://slate.com/human-interest/2018/12/against-teaching-kids-to-code-creativity-problem-solving.html&quot;&gt;I’m a Developer. I Won’t Teach My Kids to Code, and Neither Should You.​&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I agree halfway. I don’t think “teach kids to code” only means teaching the syntax of a particular language. Maybe it should mean teaching the ways of thinking that could be transferred to other activities.&lt;/p&gt;
&lt;p&gt;I love his point about teaching curiosity:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You’re teaching them the world is full of interesting things to discover. You’re showing them how to be passionate and look for that ephemeral sense of quality in everything they do. The best part is that even if they don’t become coders—most shouldn’t and won’t—the same skills can be used in nearly any career, in every hobby, in every life.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;With examples:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;One day, my son was concerned that a chair of his was wobbly. We looked at it and he helped me isolate the problem: One of the screws was loose. I found one of our many leftover hex wrenches and showed him how to screw it back in. After that, he was curious what would happen if he screwed the other way, which he did until the screw came out. We ended up taking the chair all the way apart and putting it back together a couple of times, often mismatching pieces, before he was satisfied the job was finished. Try something. See how it works. Try again.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>Norcal Renaissance Faire 2018</title><link>https://www.augusteo.com/blog/norcal-renaissance-faire-2018/</link><guid isPermaLink="true">https://www.augusteo.com/blog/norcal-renaissance-faire-2018/</guid><description>Norcal Renaissance Faire 2018 - a blog post by Victor Augusteo</description><pubDate>Tue, 02 Oct 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_8991-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_8991-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_8991-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_8991-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_8992-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_8992-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_8992-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_8992-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9000-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9000-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9002-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9002-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9002-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9002-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9003-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9003-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9003-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9003-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9004-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9004-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9037-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9037-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9037-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9037-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9041-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9041-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9041-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9041-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9044-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9044-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9046-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9046-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9046-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9046-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9052-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9052-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9052-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9052-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9054-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9054-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9055-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9055-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9055-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9055-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9056-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9056-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9056-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9056-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9057-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9057-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9058-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9058-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9058-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9058-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9060-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9060-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9060-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9060-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9061-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9061-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9063-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9063-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9063-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9063-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9065-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9065-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9065-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9065-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9068-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9068-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9073-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9073-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9073-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9073-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9075-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9075-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9075-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9075-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9076-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9076-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9078-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9078-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9078-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9078-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9083-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9083-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9083-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9083-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9084-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9084-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9089-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9089-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9089-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9089-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9091-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9091-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9091-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9091-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9093-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9093-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9098-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9098-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9098-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9098-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9100-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9100-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9100-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9100-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9103-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9103-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9106-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9106-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9106-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9106-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9107-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9107-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9107-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9107-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9113-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9113-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9115-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9115-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9115-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9115-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9117-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9117-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9117-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9117-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9118-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9118-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9119-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9119-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9119-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9119-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9122-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9122-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9122-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9122-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9124-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9124-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9128-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9128-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9128-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9128-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9130-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9130-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9130-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9130-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9131-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9131-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9134-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9134-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9134-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9134-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9137-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9137-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9137-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9137-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9138-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9138-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9142-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9142-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9142-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9142-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9144-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9144-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9146-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9146-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9147-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9147-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9148-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9148-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9152-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9152-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9154-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9154-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9156-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9156-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9160-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9160-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9161-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9161-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9165-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9165-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9167-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9167-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9169-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9169-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9173-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9173-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9174-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9174-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9175-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9175-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9176-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9176-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9178-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9178-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9184-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9184-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9186-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9186-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9187-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9187-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9190-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9190-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9201-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9201-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9203-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9203-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9205-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9205-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9209-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9209-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9210-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9210-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9212-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9212-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9213-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9213-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9214-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9214-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9217-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9217-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9220-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9220-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9222-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9222-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9223-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9223-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9225-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9225-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9227-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9227-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9230-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9230-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9236-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9236-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9242-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9242-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9243-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9243-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9246-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9246-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9248-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9248-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9249-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9249-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9252-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9252-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9253-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9253-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9259-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9259-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9260-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9260-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9262-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9262-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9264-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9264-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9267-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9267-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9270-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9270-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9271-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9271-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9277-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9277-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9280-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9280-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9285-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9285-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9291-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9291-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9294-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9294-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9297-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9297-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9303-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9303-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9306-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9306-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9307-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9307-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9310-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9310-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9314-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9314-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9319-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9319-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9322-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9322-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9328-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9328-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9332-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9332-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9333-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9333-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9337-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9337-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9340-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9340-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9344-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9344-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9348-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9348-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9350-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9350-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9352-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9352-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9355-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9355-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9356-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9356-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9362-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9362-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9364-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9364-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9366-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9366-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9369-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9369-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9371-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9371-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9374-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9374-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9376-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9376-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9379-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9379-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9382-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9382-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9386-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9386-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9394-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9394-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9394-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9394-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9396-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9396-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9396-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9396-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9400-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9400-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9402-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9402-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9402-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9402-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9406-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9406-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9406-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9406-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9411-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9411-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9412-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9412-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9412-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9412-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9432-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9432-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9432-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9432-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9436-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9436-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9444-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9444-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9444-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9444-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9445-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9445-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9445-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9445-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9450-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9450-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9454-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9454-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9454-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9454-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9457-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9457-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9457-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9457-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9460-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9460-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9461-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9461-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9461-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9461-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9463-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9463-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9463-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9463-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9465-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9465-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9467-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9467-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9467-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9467-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9473-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9473-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9473-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9473-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9475-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9475-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9476-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9476-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9476-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9476-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9477-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9477-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9477-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9477-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9483-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9483-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9484-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9484-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9484-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9484-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9486-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9486-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9486-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9486-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9489-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9489-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9496-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9496-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9496-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9496-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9498-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9498-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9498-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9498-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9514-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9514-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9515-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9515-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9515-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9515-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9520-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9520-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9520-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9520-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9523-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9523-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9524-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9524-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9524-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9524-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9530-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9530-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9530-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9530-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9534-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9534-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9535-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9535-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9535-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9535-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9536-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9536-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9536-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9536-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9537-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9537-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9541-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9541-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9541-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9541-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9542-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9542-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9542-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9542-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9544-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9544-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9548-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9548-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9548-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9548-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9550-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9550-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9550-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9550-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9551-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9551-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9553-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9553-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9553-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9553-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9561-thumb.jpg&quot; alt=&quot;norcal-renaissance-faire-2018-IMG_9561-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![norcal-renaissance-faire-2018-IMG_9561-thumb 1.jpg](https://www.augusteo.com/assets/blog/norcal-renaissance-faire-2018/norcal-renaissance-faire-2018-IMG_9561-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;License:&lt;/p&gt;
</content:encoded></item><item><title>OpenAI Five - Study Notes</title><link>https://www.augusteo.com/blog/openai-five/</link><guid isPermaLink="true">https://www.augusteo.com/blog/openai-five/</guid><description>OpenAI Five - Study Notes - a blog post by Victor Augusteo</description><pubDate>Sun, 08 Jul 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;This blog post is my study notes on OpenAI Five. I am not involved in the research effort.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Why Dota?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;objective -&amp;gt; destroy enemy’s throne. to do that you have to destroy the 4 layers of defensive towers while the other team is defending them/attacking your tower.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/openai-five/openai-five-tOZJkICRr-7xoEcAZ-sRcx2kQbR2_YjHmfT6WYdnJ5s.jpg&quot; alt=&quot;openai-five-tOZJkICRr-7xoEcAZ-sRcx2kQbR2_YjHmfT6WYdnJ5s.jpg&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There are 115+ (and growing) unique heroes with unique spells and play styles. Each hero has access to 3 spells and one (usually long cooldown) ultimate spell.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Established game with millions of player worldwide. It was released the year 2003 as a map of Blizzard’s Warcraft 3 game.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/openai-five/openai-five-maxresdefault.jpg&quot; alt=&quot;openai-five-maxresdefault.jpg&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;IceFrog was then hired by Valve to make Dota 2.  Valve is currently raising fund for ‘The International 2018’ &lt;a href=&quot;http://www.dota2.com/international/battlepass/&quot;&gt;prize pool&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/openai-five/openai-five-Dota-2.jpg&quot; alt=&quot;openai-five-Dota-2.jpg&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Super popular e-sports with largest-prize pool tournament in the world.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/openai-five/openai-five-Screen20Shot202018-07-0620at209.18.4620am.jpg&quot; alt=&quot;openai-five-Screen20Shot202018-07-0620at209.18.4620am.jpg&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;With top players &lt;a href=&quot;https://www.esportsearnings.com/games/231-dota-2/top-players&quot;&gt;earning millions of dollars&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/openai-five/openai-five-Screen20Shot202018-07-0820at201.00.5620pm.jpg&quot; alt=&quot;openai-five-Screen20Shot202018-07-0820at201.00.5620pm.jpg&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Very complex game&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OpenAI Five observes every fourth frame, yielding 20,000 moves. &lt;a href=&quot;https://blog.ebemunk.com/a-visual-look-at-2-million-chess-games/&quot;&gt;Chess&lt;/a&gt; usually ends before 40 moves, &lt;a href=&quot;https://en.wikipedia.org/wiki/Go_and_mathematics#Game_tree_complexity&quot;&gt;Go&lt;/a&gt; before 150 moves, with almost every move being strategic&lt;/li&gt;
&lt;li&gt;there is an average of ~1,000 valid actions each tick. The average &lt;a href=&quot;https://en.wikipedia.org/wiki/Branching_factor&quot;&gt;number of actions&lt;/a&gt; in chess is 35; in Go, 250.&lt;/li&gt;
&lt;li&gt;Valve’s &lt;a href=&quot;https://developer.valvesoftware.com/wiki/Dota_Bot_Scripting&quot;&gt;Bot API&lt;/a&gt; shows as many as 20,000 (mostly floating-point) numbers representing all information a human is allowed to access. A chess board is naturally represented as about 70 enumeration values; a Go board as about 400 enumeration values.&lt;/li&gt;
&lt;li&gt;I started playing this game more than 13 years ago when I was still in high school/college. The game has been actively developed for over a decade. The game also gets an update about once every two weeks, constantly changing the environment semantics.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Previous work:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;1v1 bot - &lt;a href=&quot;https://blog.openai.com/dota-2/&quot;&gt;Dota 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;simpler version that only works for 1v1. With a single hero (Shadow Fiend).&lt;/li&gt;
&lt;li&gt;result: beat all contestants, who themselves are top Dota players in the world.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Related research effort&lt;/h2&gt;
&lt;h2&gt;What is OpenAI Five?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;It’s a team of five neural networks that can work together to fight five human players in the game of Dota.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;hardware stats:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/openai-five/openai-five-Screen20Shot202018-07-0720at2010.26.2620pm.jpg&quot; alt=&quot;openai-five-Screen20Shot202018-07-0720at2010.26.2620pm.jpg&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The 256 P100 optimizers are less than $400/hr. You can rent 128000 preemptible vcpus for another $1280/hr. Toss in some more support GPUs, and we’re at maybe $2500/hr all in.&lt;/li&gt;
&lt;li&gt;that is $57,600/day&lt;/li&gt;
&lt;li&gt;they have trained, 2+ months, so around $4 million so far just for hardware.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;learns using a massively-scaled version of &lt;a href=&quot;https://blog.openai.com/openai-baselines-ppo/&quot;&gt;Proximal Policy Optimization&lt;/a&gt; PPO is a class of reinforcement learning algorithm&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/openai-five/openai-five-math_PPO_5-1.png&quot; alt=&quot;openai-five-math_PPO_5-1.png&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Each of &lt;a href=&quot;https://d4mucfpksywv.cloudfront.net/research-covers/openai-five/network-architecture.pdf&quot;&gt;OpenAI Five’s networks&lt;/a&gt; contain a single-layer, 1024-unit &lt;a href=&quot;http://colah.github.io/posts/2015-08-Understanding-LSTMs/#lstm-networks&quot;&gt;LSTM&lt;/a&gt; that sees the current game state (extracted from Valve’s &lt;a href=&quot;https://developer.valvesoftware.com/wiki/Dota_Bot_Scripting&quot;&gt;Bot API&lt;/a&gt; ) and emits actions through several possible action heads. Each head has semantic meaning, for example, the number of ticks to delay this action, which action to select, the X or Y coordinate of this action in a grid around the unit, etc. Action heads are computed independently.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/openai-five/openai-five-Screen20Shot202018-07-0720at2011.10.1820pm.jpg&quot; alt=&quot;openai-five-Screen20Shot202018-07-0720at2011.10.1820pm.jpg&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Current set of restrictions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;5 invulnerable couriers, no exploiting them by scouting or tanking&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Even with OpenAI restrictions, there are hundreds of items, dozens of buildings, spells, and unit types, and a long tail of game mechanics to learn about — many of which yield powerful combinations. It’s not easy to explore this combinatorially-vast space efficiently.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;OpenAI Five learns from self-play (starting from random weights), which provides a natural curriculum for exploring the environment. To avoid “strategy collapse”, the agent trains 80% of its games against itself and the other 20% against its past selves. In the first games, the heroes walk aimlessly around the map. After several hours of training, concepts such as &lt;a href=&quot;https://www.reddit.com/r/DotA2/comments/17fj2y/laning_101/&quot;&gt;laning&lt;/a&gt; , &lt;a href=&quot;https://dota2.gamepedia.com/Farming&quot;&gt;farming&lt;/a&gt; , or fighting over &lt;a href=&quot;https://pvgna.com/dota2/paths/how-to-master-mid-lane&quot;&gt;mid&lt;/a&gt; emerge. After several days, they consistently adopt basic human strategies: attempt to steal &lt;a href=&quot;https://dota2.gamepedia.com/Bounty_Rune&quot;&gt;Bounty&lt;/a&gt; runes from their opponents, walk to their &lt;a href=&quot;https://dota2.gamepedia.com/Buildings#Towers&quot;&gt;tier one&lt;/a&gt; towers to farm, and rotate heroes around the map to gain lane advantage. And with further training, they become proficient at high-level strategies like &lt;a href=&quot;https://www.reddit.com/r/DotA2/comments/4iyr00/how_do_you_counter_a_5man_early_game_push_strat/&quot;&gt;5-hero push&lt;/a&gt; .&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;OpenAI Five does not contain an explicit communication channel between the heroes’ neural networks. Teamwork is controlled by a hyperparameter OpenAI dubbed “team spirit”. Team spirit ranges from 0 to 1, putting a weight on how much each of OpenAI Five’s heroes should care about its individual reward function versus the average of the team’s reward functions. OpenAI anneals its value from 0 to 1 overtraining.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Training Methods&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;implemented as a general-purpose RL training system called Rapid, which can be applied to any &lt;a href=&quot;https://github.com/openai/gym&quot;&gt;Gym&lt;/a&gt; environment. OpenAI used Rapid to solve other problems at OpenAI, including &lt;a href=&quot;https://blog.openai.com/competitive-self-play/&quot;&gt;Competitive Self-Play&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;gym: &lt;a href=&quot;https://gym.openai.com/&quot;&gt;Gym&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Gym is a toolkit for developing and comparing reinforcement learning algorithms. It supports teaching agents everything from &lt;a href=&quot;https://gym.openai.com/envs/Humanoid-v1&quot;&gt;walking&lt;/a&gt; to playing games like &lt;a href=&quot;https://gym.openai.com/envs/Pong-ram-v0&quot;&gt;Pong&lt;/a&gt; or &lt;a href=&quot;https://gym.openai.com/envs/VideoPinball-ram-v0&quot;&gt;Pinball&lt;/a&gt; .&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.openai.com/competitive-self-play/&quot;&gt;Competitive Self-Play&lt;/a&gt; allows simulated AIs to discover physical skills like tackling, ducking, faking, kicking, catching, and diving for the ball, without explicitly designing an environment with these skills in mind. Self-play ensures that the environment is always the right difficulty for an AI to improve.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The training system is separated into rollout workers, which run a copy of the game and an agent gathering experience, and optimizer nodes, which perform synchronous gradient descent across a fleet of GPUs. The rollout workers sync their experience through Redis to the optimizers. Each experiment also contains workers evaluating the trained agent versus reference agents, as well as monitoring software such as &lt;a href=&quot;https://www.tensorflow.org/programmers_guide/summaries_and_tensorboard&quot;&gt;TensorBoard&lt;/a&gt; , &lt;a href=&quot;https://sentry.io/welcome/&quot;&gt;Sentry&lt;/a&gt; , and &lt;a href=&quot;https://grafana.com/&quot;&gt;Grafana&lt;/a&gt; .&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/openai-five/openai-five-rapid-architecture2x--1-.png&quot; alt=&quot;openai-five-rapid-architecture2x--1-.png&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;During synchronous gradient descent, each GPU computes a gradient on its part of the batch, and then the gradients are globally averaged. OpenAI originally used &lt;a href=&quot;https://en.wikipedia.org/wiki/Message_Passing_Interface&quot;&gt;MPI’s&lt;/a&gt; &lt;a href=&quot;http://www.mcs.anl.gov/research/projects/mpi/mpi-standard/mpi-report-1.1/node82.htm&quot;&gt;allreduce&lt;/a&gt; for averaging, but now use OpenAI own &lt;a href=&quot;https://developer.nvidia.com/nccl&quot;&gt;NCCL2&lt;/a&gt; wrappers that parallelize GPU computations and network data transfer.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;NCCL2 = The NVIDIA Collective Communications Library (NCCL) implements multi-GPU and multi-node collective communication primitives that are performance optimized for NVIDIA GPUs. NCCL provides routines such as all-gather, all-reduce, broadcast, reduce, reduce-scatter, that are optimized to achieve high bandwidth over PCIe and NVLink high-speed interconnect.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The latencies for synchronizing 58MB of data (size of OpenAI Five’s parameters) across different numbers of GPUs are shown on the right. The latency is low enough to be largely masked by GPU computation which runs in parallel with it.  OpenAI implemented Kubernetes, Azure, and GCP backends for Rapid.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/openai-five/openai-five-gpu-synchronization-time-small2x.png&quot; alt=&quot;openai-five-gpu-synchronization-time-small2x.png&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Reward Functions&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Each hero’s reward is a linear combination of separate signals from the game. Each of these signals produces a score, and the agent’s reward is the increase in score from one tick to the next.&lt;/li&gt;
&lt;li&gt;These signals and weights were designed by OpenAI local Dota experts at the start, and have only been tweaked a handful of times since.
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Individual Scores&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Building Scores&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lane Assignments&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;agent receives a special reward to encourage exploration called “lane assignments.” During training, OpenAI assigns each hero a subset of the three lanes in the game. The model observes this assignment, and receives a negative reward (-0.02) if it leaves the designated lanes early in the game.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zero Sum&lt;/strong&gt; Each team’s mean reward is subtracted from the rewards of the enemy team to ensure that the sum of all ten rewards is zero, thereby preventing the two teams from finding positive-sum situations. It also ensures that whenever OpenAI assigns reward for a signal like “killing a tower,” the agent automatically receives a comparable signal for the reverse; “defending a tower that would have died.” &lt;code&gt;hero_rewards[i] -= mean(enemy_rewards)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Team Spirit&lt;/strong&gt; OpenAI wants them to take into account their teammates’ situations, rather than greedily optimizing for their own reward. For this reason, OpenAI average the reward across the team’s heroes using a hyperparameter τ called “team spirit”: &lt;code&gt;hero_rewards[i] = τ * mean(hero_rewards) + (1 - τ) * hero_rewards[i]&lt;/code&gt; OpenAI anneals τ from 0.2 at the start of training to 0.97 at the end of OpenAI current experiment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Time Scaling&lt;/strong&gt; The majority of reward mass in OpenAI agent’s rollout experience comes from the later part of the game. However, the early game can be very important; if the agent plays badly at the start, it can be hard to recover. OpenAI wish OpenAI training scheme to place sufficient importance on the early part of the game. For this reason OpenAI scales up all rewards early in the game and scale down rewards late in the game, by multiplying all rewards by: &lt;code&gt;hero_rewards[i] *= 0.6 ** (T/10 min)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Training progression&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;teams:&lt;/strong&gt; 1. Best OpenAI employee team: 2.5k &lt;a href=&quot;https://dota2.gamepedia.com/Matchmaking_Rating&quot;&gt;MMR&lt;/a&gt; (46th percentile) 2. Best audience players watching OpenAI employee match (including Blitz, who commentated the first OpenAI employee match): 4-6k MMR (90th-99th percentile), though they’d never played as a team. 3. Valve employee team: 2.5-4k MMR (46th-90th percentile). 4. Amateur team: 4.2k MMR (93rd percentile), trains as a team. 5. Semi-pro team: 5.5k MMR (99th percentile), trains as a team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Versions:&lt;/strong&gt; * The April 23rd version of OpenAI Five was the first to beat OpenAI scripted baseline. * The May 15th version of OpenAI Five was evenly matched versus team 1, winning one game and losing another. * The June 6th version of OpenAI Five decisively won all its games versus teams 1-3. OpenAI set up informal &lt;a href=&quot;https://www.reddit.com/r/DotA2/comments/3s9zet/what_is_a_scrim/&quot;&gt;scrims&lt;/a&gt; with teams 4 &amp;amp; 5, expecting to lose soundly, but OpenAI Five won two of its first three games versus both.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt; * Repeatedly sacrificed its own &lt;a href=&quot;https://dota2.gamepedia.com/Lane&quot;&gt;safe lane&lt;/a&gt; (top lane for dire; bottom lane for radiant) in exchange for controlling the enemy’s safe lane, forcing the fight onto the side that is harder for their opponent to defend. This strategy emerged in the professional scene in the last few years, and is now considered to be the prevailing tactic. * Pushed the &lt;a href=&quot;https://purgegamers.true.io/purge/phases-of-the-game/&quot;&gt;transitions&lt;/a&gt; from early- to mid-game faster than its opponents. It did this by: (1) setting up successful &lt;a href=&quot;https://dota2.gamepedia.com/Ganking&quot;&gt;ganks&lt;/a&gt; (when players move around the map to ambush an enemy hero — see animation) when players overextended in their lane, and (2) by grouping up to take towers before the opponents could organize a counterplay. * Deviated from current &lt;a href=&quot;https://liquipedia.net/dota2/Metagame&quot;&gt;playstyle&lt;/a&gt; in a few areas, such as giving &lt;a href=&quot;https://dota2.gamepedia.com/Role#Support&quot;&gt;support&lt;/a&gt; heroes (which usually do not take priority for resources) lots of early experience and gold. OpenAI Five’s prioritization allows for its damage to peak sooner and push its advantage harder, winning team fights and capitalizing on mistakes to ensure a fast win.&lt;/p&gt;
&lt;h2&gt;Difference versus humans&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;the bot instantly sees data like positions, healths, and item inventories that humans have to check manually. because just rendering pixels from the game would require thousands of GPUs. this is cost saving method.&lt;/li&gt;
&lt;li&gt;OpenAI Five averages around 150-170 actions per minute (and has a theoretical maximum of 450 due to observing every 4th frame). Frame-perfect timing, while &lt;a href=&quot;https://www.reddit.com/r/DotA2/comments/493wib/frameperfect_stun_by_swindle_on_enchantress_in_eg/&quot;&gt;possible&lt;/a&gt; for skilled players, is trivial for OpenAI Five. OpenAI Five has an average reaction time of 80ms, which is faster than humans.
&lt;ul&gt;
&lt;li&gt;different heroes have a different need for higher APM.&lt;/li&gt;
&lt;li&gt;the 5 heroes that OpenAI chose are considered ‘beginner’ heroes in that they require fairly low APM to play.&lt;/li&gt;
&lt;li&gt;all the 5 heroes also are ranged heroes who are considered easier to play.&lt;/li&gt;
&lt;li&gt;higher APM heroes example are &lt;a href=&quot;https://dota2.gamepedia.com/Invoker&quot;&gt;Invoker - Dota 2 Wiki&lt;/a&gt;, &lt;a href=&quot;https://dota2.gamepedia.com/Meepo&quot;&gt;Meepo - Dota 2 Wiki&lt;/a&gt; or &lt;a href=&quot;https://dota2.gamepedia.com/Chen&quot;&gt;Chen - Dota 2 Wiki&lt;/a&gt; due to having many skills (invoker has 14) and controlling many units at once (Chen’s creeps or meepo’s clones)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Surprising Findings&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Binary rewards can give good performance.&lt;/strong&gt; OpenAI 1v1 model had a shaped reward, including rewards for last hits, kills, and the like. OpenAI ran an experiment where OpenAI only rewarded the agent for winning or losing, and it trained an order of magnitude slower and somewhat plateaued in the middle, in contrast to the smooth learning curves OpenAI usually see. The experiment ran on 4,500 cores and 16 k80 GPUs, training to the level of semi-pros (70 &lt;a href=&quot;https://en.wikipedia.org/wiki/TrueSkill&quot;&gt;TrueSkill&lt;/a&gt; ) rather than 90 TrueSkill of OpenAI best 1v1 bot).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Binary rewards (win/loss score at the end of the rollout) scored a “good” 70.&lt;/li&gt;
&lt;li&gt;With sparse reward scored a better 90 and learned much faster.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Creep blocking can be learned from scratch. For 1v1, OpenAI learned &lt;a href=&quot;https://blog.openai.com/more-on-dota-2/#thetask&quot;&gt;creep blocking&lt;/a&gt; using traditional RL with a “creep block” reward.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;bugs: large negative reward for reaching level 25. It turns out it’s possible to beat good humans while still hiding serious bugs!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/openai-five/openai-five-bug-comparison-small2x.png&quot; alt=&quot;openai-five-bug-comparison-small2x.png&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;next steps&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dota2.gamepedia.com/The_International_2018&quot;&gt;The International 2018 - Dota 2 Wiki&lt;/a&gt; where OpenAI might battle the all-star or winning team like last year’s 1v1 bot test.&lt;/li&gt;
&lt;li&gt;July 28th - OpenAI will try to battle a team of top players&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;discussions:&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=17392455&quot;&gt;OpenAI Five | Hacker News&lt;/a&gt; &lt;a href=&quot;https://www.reddit.com/r/programming/comments/8tse7u/openai_five_5v5_dota_2_bots/&quot;&gt;https://www.reddit.com/r/programming/comments/8tse7u/openai_five_5v5_dota_2_bots/&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Can &apos;Interview vs Audition&apos; Approach Works for Software Engineers?</title><link>https://www.augusteo.com/blog/interview-audition/</link><guid isPermaLink="true">https://www.augusteo.com/blog/interview-audition/</guid><description>Can &apos;Interview vs Audition&apos; Approach Works for Software Engineers? - a blog post by Victor Augusteo</description><pubDate>Sun, 13 May 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/interview-audition/interview-audition-xConducting-job-interview-7720-600x400.jpg.pagespeed.ic.BWuOH5BDTh.jpg&quot; alt=&quot;interview-audition-xConducting-job-interview-7720-600x400.jpg.pagespeed.ic.BWuOH5BDTh.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This article from Ryan Holiday has been making rounds lately:&lt;/p&gt;
&lt;h2&gt;Summary:&lt;/h2&gt;
&lt;p&gt;There are two types of football coaches, either they sit down and expects a barrage of questions from management or the one doing most of the asking. The first type is the typical interview while the second type conducts an audition. Ramit Sethi has an excellent video explaining the approach here: &lt;a href=&quot;https://www.youtube.com/watch?v=NViqHCrljf8&quot;&gt;The Briefcase Technique - YouTube&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Software Engineering Context&lt;/h2&gt;
&lt;p&gt;After all the brouhaha about the broken tech interview, the practice is still going strong. The problem stems from the lack of credibility of resumes. Just ask any seasoned interviewer at big tech companies, they will tell you impressive sounding resumes are dimes a dozen. Once those people got in and asked to solve a simple problem, a significant portion of them couldn’t produce the great code expected. That is the single thing a software engineer will do the most: write code. So it makes sense to see them write code.&lt;/p&gt;
&lt;p&gt;The software interview process is not unlike actor audition. Actors still have to come in to act out parts of the script as if it is in front of a real camera and audience.&lt;/p&gt;
&lt;p&gt;Now can someone audition as software engineer? the answer is partly.&lt;/p&gt;
&lt;p&gt;Many companies have started the audition approach where engineers could submit their resume to one company (e.g. &lt;a href=&quot;https://hired.com/&quot;&gt;Hired&lt;/a&gt; or &lt;a href=&quot;https://triplebyte.com/&quot;&gt;Triplebyte&lt;/a&gt;) and get matched with a lot of companies wanting to interview them. This process makes the first step of the job hunt - getting the interview - to be straightforward. They don’t have to submit resume to one thousand companies and hope one call back.&lt;/p&gt;
&lt;p&gt;On the on-site interview, the traditional process still works best. The interviewee should expect the typical coding, architecture, logic and algorithm questions. The briefcase technique mentioned above would work here because company codebases and plans are typically closed, meaning the candidate won’t be able to research them beforehand to make suggestions and assertions. Also, companies wouldn’t like it when a candidate who knows nothing of their codebase says that they need to rewrite their whole stack with Javascript.&lt;/p&gt;
&lt;p&gt;However, at every interview, there’s always that Q&amp;amp;A time in the end. The prepared candidate can shine here. Research the company and prepare a bunch of questions. Show interest in the niche.&lt;/p&gt;
&lt;p&gt;I can tell you that this doesn’t work in software as well as another field because even though the candidate asks outstanding questions, they will still flunk the interview if they couldn’t solve the coding problems well. The Q&amp;amp;A is just icing on the cake.&lt;/p&gt;
</content:encoded></item><item><title>What Is Bodpod and How Accurate Is It?</title><link>https://www.augusteo.com/blog/bodpod/</link><guid isPermaLink="true">https://www.augusteo.com/blog/bodpod/</guid><description>What Is Bodpod and How Accurate Is It? - a blog post by Victor Augusteo</description><pubDate>Fri, 04 May 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Yesterday, I used Bodpod for the first time. It was $10 per scan at my company, which was very cheap compared to the industry standard DEXA scan, albeit less accurate.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/bodpod/bodpod-F06D887F-645B-4077-9F46-9D8680333AE7.png&quot; alt=&quot;bodpod-F06D887F-645B-4077-9F46-9D8680333AE7.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You have to wear very tight fitting clothes like a swimwear as to not screw with the measurement.&lt;/p&gt;
&lt;p&gt;There is two thirty-second measurement, and the result comes out straight away. You will get bodyweight, lean mass and body fat percentage.&lt;/p&gt;
&lt;p&gt;I was quite surprised to see that I had higher body fat percentage compared to what I expected but lost 6lbs since early this year. Does it mean I had even more body fat before? Is it even accurate?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0115086&quot;&gt;This scientific paper&lt;/a&gt; compares BodPod with DEXA scan and find that BodPod tends to underestimate the BF% of high BF individual and underestimate low BF individual. This means that my real BF% might even be higher than what was measured at this time.&lt;/p&gt;
&lt;p&gt;BodPod works with evaluating the air displacement in the tank. Breathing or moving might affect the measurement. The system will then calculate the relationship between pressure and volume using predetermined formula. Because of this, the result tends to be less accurate than DEXA which uses x-ray to get high accuracy measurement.&lt;/p&gt;
&lt;p&gt;I’m leaning towards just skipping BodPod and using DEXA in the future for tracking progress.&lt;/p&gt;
&lt;p&gt;Regardless of the accuracy, this measurement still shows that I’m still far off from my target leanness and will be adding more cardio and HIIT on top of my lifting routine.&lt;/p&gt;
</content:encoded></item><item><title>5 Proven Ways That Your Brain Is Lying to You - Undoing Project Book Review &amp; Summary</title><link>https://www.augusteo.com/blog/undoing-project/</link><guid isPermaLink="true">https://www.augusteo.com/blog/undoing-project/</guid><description>5 Proven Ways That Your Brain Is Lying to You - Undoing Project Book Review &amp; Summary - a blog post by Victor Augusteo</description><pubDate>Mon, 23 Apr 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/undoing-project/undoing-project-F68F98DE-7AE2-4F1D-90C2-C883CDAD75A5.png&quot; alt=&quot;undoing-project-F68F98DE-7AE2-4F1D-90C2-C883CDAD75A5.png&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Review&lt;/h2&gt;
&lt;p&gt;An excellent biography of Behavioural Economics and its fathers. Before Daniel Kahneman and Amos Tversky, economics (and economist) has always assumed that human is mostly rational being. We used to believe that human will make the best decision based on available information at the time, and those decisions reproducible. Now we know that that is untrue. In the words of Dan Ariely, we are ‘predictably irrational’.&lt;/p&gt;
&lt;p&gt;You would learn about the relationship of Danny and Amos, how their personality and work dynamic would result in one of the most prominent psychology and economic theory. Lewis interestingly wrote the journey, starting from their childhood to how the war in Israel affects their work together.&lt;/p&gt;
&lt;p&gt;Lewis started the book with a story of his ‘Moneyball’ approach in NBA. I think this is the most significant weakness of the book because this chapter does not tie together with the rest. Once you started the next chapter, it felt like you are reading an entirely different book. It would have been better if he removed it.&lt;/p&gt;
&lt;p&gt;I love the book and rate it a solid 4 out of 5 stars.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;This book is a hard one to summarise because of its narrative and density. It reads like a joint biography but also contains a significant amount of distillable knowledge. I will try my best to summarise both.&lt;/p&gt;
&lt;h3&gt;Moneyball in the NBA&lt;/h3&gt;
&lt;p&gt;In NBA, scouts relied on their gut feeling when selecting new players for drafting. They were not aware of the many biases affecting that feeling, resulting in many bad decisions over the years.&lt;/p&gt;
&lt;p&gt;Daryl Morey introduced the new way of analysing players using statistical data. He even avoided interviewing because he thinks the face to face meeting would affect the decision too much. This new approach worked well, but it is still not impervious to human bias. One of the most famous cases was Jeremy Lin, quoted from the book:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“He lit up our model,” said Morey. “Our model said take him with, like, the 15th pick in the draft.” The objective measurement of Jeremy Lin didn’t square with what the experts saw when they watched him play; a not terribly athletic Asian kid. Morey hadn’t completely trusted his model - and so had chickened out and not drafted Lin. A year after the Houston Rockets failed to draft Jeremy Lin, they began to measure the speed of a player’s first two steps: Jeremy Lin had the quickest first move of any player measured. He was explosive and was able to change direction far more quickly than most NBA players. “He’s incredibly athletic,” said Morey. “But the reality is that every person, including me, thought he was unathletic. And I can’t think of any reason for it other than he was Asian.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The incident led to Morey improving his model even more and added more rules to his scouts. They can only make a comparison if the players are from a different racial background, so more comparing new black athlete with Michael Jordan or comparing Asian athlete with Yao Ming. Most of the time, the favourable comparison disappears.&lt;/p&gt;
&lt;h3&gt;Myth of Rational Actor, Biases and Heuristics&lt;/h3&gt;
&lt;p&gt;The belief that people mostly make the right decision given specific information after calculating the utility and value given the cost was prevalent in economics with the concept of Adam Smith’s &lt;em&gt;invisible hand&lt;/em&gt;. It influences all sorts of government policy and decision making.&lt;/p&gt;
&lt;p&gt;Danny and Amos did not originally set out just to disprove this, but the data steers them towards this conclusion. Along with their studies, they found that people use heuristics to make quick judgments instead of rational thinking. We will explore several examples of them:&lt;/p&gt;
&lt;h3&gt;1. Confirmation Bias&lt;/h3&gt;
&lt;p&gt;When a basketball scout likes a certain player, they will organise data to support their findings which is why the first impression is critical. The scouts usually made matters worse because they tend to favour players that resemble their younger selves, even though it is irrelevant to the analysis.&lt;/p&gt;
&lt;p&gt;People do this all the time without realising. This bias gets exploited all the time in the political arena. Once a person is committed to a party or candidate, they will start to seek reasons to support their party and reasons to vilify the opponent. Over time, a great divide would polarise the nation further and further away. Two person with opposing viewpoint could look at an event and come to two widely different perception due to their bias.&lt;/p&gt;
&lt;h3&gt;2. Anchoring&lt;/h3&gt;
&lt;p&gt;People would anchor their future decision based on arbitrary priors. For example:&lt;/p&gt;
&lt;p&gt;Two groups were asked to estimate the result of 8! (eight factorial). One of the group was presented &lt;code&gt;1x2x3x4x5x6x7x8&lt;/code&gt; while the other got &lt;code&gt;8x7x6x5x4x3x2x1&lt;/code&gt;. The second group’s answer was reliably around five times larger than the firsts.&lt;/p&gt;
&lt;p&gt;You could even anchor an answer to a completely random thing, like the last digits of a telephone number. People with telephone ending in 98 would give a much higher estimate compared to people with phone finishing with 06.&lt;/p&gt;
&lt;h3&gt;3. Present and Hindsight Bias&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Present Bias&lt;/strong&gt; says that people tend to undervalue the future with the present. People prefer cash today even though investing it will net more gains over the years.&lt;/p&gt;
&lt;p&gt;The Present Bias is amplified by &lt;strong&gt;Hindsight Bias&lt;/strong&gt; in which people tends to look at an unpredictable outcome and assume it was predictable all along. For example, suppose that the market just crashed last week. The people who bought a new car last month instead of investing the money would think to themselves that their hunch was right and they knew the crash was coming all along.&lt;/p&gt;
&lt;h3&gt;4. Availability Heuristics&lt;/h3&gt;
&lt;p&gt;Availability Heuristics is the bias that made many people avoid the news nowadays. People would make the judgement error of assuming that because it is easy to recall the instance of something happening, the more likely is that thing being true.&lt;/p&gt;
&lt;p&gt;For example, the news covers death by terrorism en masse, thus increasing its ‘availability’. People who watch the headlines would think terrorism is on the rise and causing massive damages, even though the actual number of death is minimal compared to under-reported heart disease deaths.&lt;/p&gt;
&lt;p&gt;Owen Shen made a fantastic graphical representation of &lt;a href=&quot;https://owenshen24.github.io/charting-death/&quot;&gt;Death: Reality vs Reported&lt;/a&gt; in which he compares the amount of reporting compared to the actual deaths. You should read his article in its entirety.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/undoing-project/undoing-project-B0F100FD-5F5F-4862-B3C9-5A3F59860B0C.png&quot; alt=&quot;undoing-project-B0F100FD-5F5F-4862-B3C9-5A3F59860B0C.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;image credit: &lt;a href=&quot;https://owenshen24.github.io/charting-death/&quot;&gt;Owen Shen&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;These heuristics lead people to both ‘excessive fear and unjustified complacency’. Thus we have to be very careful with it.&lt;/p&gt;
&lt;h3&gt;5. Loss Aversion, Prospect Theory and Endowment Effect&lt;/h3&gt;
&lt;p&gt;These three heuristics are tightly intertwined. &lt;strong&gt;Endowment Effect&lt;/strong&gt; is when people value things that they have much more than things that they do not, just because they own them which leads to &lt;strong&gt;Loss Aversion&lt;/strong&gt;, which says that losses hurt more than gains feel good. We can estimate the pain of losses or joy of gains with this graph:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/undoing-project/undoing-project-98E3E9D8-3481-4A5B-B8C4-3A986535C466.png&quot; alt=&quot;undoing-project-98E3E9D8-3481-4A5B-B8C4-3A986535C466.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;image credit: &lt;a href=&quot;https://en.wikipedia.org/wiki/Prospect_theory#/media/File:Valuefun.jpg&quot;&gt;wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;They conducted a study with the following questions:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;First situation: You have $1,000, and you must pick one of the following choices: Choice A: You have a 50% chance of gaining $1,000, and a 50% chance of winning $0. Choice B: You have a 100% chance of winning $500.&lt;/p&gt;
&lt;p&gt;Second situation: You have $2,000, and you must pick one of the following choices: Choice A: You have a 50% chance of losing $1,000, and 50% of losing $0. Choice B: You have a 100% chance of losing $500.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;People would overwhelmingly pick B for the first situation and A for the second situation. This trend follows the asymmetrical value distribution of the &lt;strong&gt;Prospect Theory&lt;/strong&gt; curve.&lt;/p&gt;
&lt;h3&gt;More Cognitive Biases&lt;/h3&gt;
&lt;p&gt;To learn more cognitive biases, please visit this &lt;a href=&quot;https://betterhumans.coach.me/cognitive-bias-cheat-sheet-55a472476b18&quot;&gt;Betterhuman article&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Danny and Amos&lt;/h3&gt;
&lt;p&gt;Both were Israeli geniuses. Amos served as paratrooper while Danny escapes Nazi-occupied France to design a lot of the Israeli army psychology tests.&lt;/p&gt;
&lt;p&gt;Amos was very confrontational while Danny avoids confrontation. Amos was an extrovert while Danny introvert. Amos was an outright optimist, while Danny was a pessimist.&lt;/p&gt;
&lt;p&gt;Amos used to say, “when you are a pessimist, and the bad thing happens, you live it twice. Once when you worry about it, and the second time when it happens.” while Danny thinks that when you expect the worst, you are never disappointed.&lt;/p&gt;
&lt;p&gt;They had very little in common besides having studied at the Hebrew University in Jerusalem together. However, they proved to be each other’s perfect match, until almost to the end.&lt;/p&gt;
&lt;p&gt;Amos got the more prestigious job offers from Harvard and Stanford (which he took). Danny got a job at University of British Columbia, Vancouver, Canada which is of lower prestige compared to Amos. After their falling out, they begin to act like a divorced couple, trying to stay out of each other’s way and started to work with other people.&lt;/p&gt;
&lt;p&gt;Daniel Kahneman won the Nobel prize in Economics for his works on human behaviours, which might have been shared with Amos Tversky had Amos not been taken away by cancer. Nobel prize does not get awarded posthumously. Kahneman wrote one of my favourite books of all time: “Thinking Fast and Slow” which explores a lot of the heuristics and biases that we talked about here but in much more detail.&lt;/p&gt;
&lt;p&gt;Danny and Amos’s works arguably shaped a lot of current policies. Changing the default in retirement savings increases the saving rate accumulation in general population. People also start to pay attention to car accident rate when drivers were distracted on cellphones or drive too fast, resulting in law change to make road travel safer.&lt;/p&gt;
</content:encoded></item><item><title>The Experience of Moving From Australia to Work in Silicon Valley</title><link>https://www.augusteo.com/blog/experience-moving-aussie-to-sv/</link><guid isPermaLink="true">https://www.augusteo.com/blog/experience-moving-aussie-to-sv/</guid><description>The Experience of Moving From Australia to Work in Silicon Valley - a blog post by Victor Augusteo</description><pubDate>Wed, 11 Apr 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/experience-moving-aussie-to-sv/experience-moving-aussie-to-sv-Silicon-Valley-Tech-Campus-by-HOK-09.jpg&quot; alt=&quot;experience-moving-aussie-to-sv-Silicon-Valley-Tech-Campus-by-HOK-09.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;image &lt;a href=&quot;http://aasarchitecture.com/2014/06/silicon-valley-tech-campus-hok.html/silicon-valley-tech-campus-by-hok-09&quot;&gt;credit&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I moved here November last year from Australia. There are many questions from friends in Aussie, but the most frequent are:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“How’s life in Silicon Valley?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now that I have lived here for four months, I would try to paint the picture for you.&lt;/p&gt;
&lt;h2&gt;The Work&lt;/h2&gt;
&lt;p&gt;I was expecting a lot, but workplace has exceeded my expectation.&lt;/p&gt;
&lt;p&gt;The calibre of the people here is incredible. My big boss is a professor at a world-class university. My manager was one of the original authors of a world-famous javascript web framework that I used to use back in Aussie. There are many PhDs in my team with papers which have been cited thousands of times. We have a weekly meeting discussing the latest scientific journals in Machine Learning and AI that mostly just went over my head.&lt;/p&gt;
&lt;p&gt;It is glorious.&lt;/p&gt;
&lt;p&gt;I have learned so much in just four months. We have people who wrote entirely new data structure just because there aren’t any which could solve our unique problem. Some people have 3-D printed custom enclosure, designed and printed PCBs, then coded the micro-controller and putting it all together, just for fun.&lt;/p&gt;
&lt;p&gt;You hear about people like this on the internet. Being on the same team and working together with them is something else. The situation forces you to bring the best out of yourself and to step up to their level. It is challenging and fun.&lt;/p&gt;
&lt;p&gt;The feeling that your everyday work helping to hundreds of millions of people navigate their modern life is very fulfilling. Because of the scale, the bugs that only happen to 0.1 percent of customers would mean millions of people having a bad experience. The highs are higher, but the pressure is much higher too.&lt;/p&gt;
&lt;p&gt;I am fortunate to be working with a team that deal with many parts of the ecosystem from the desktop, the phone to the watch. I have reached out to many people whom with questions about their domain and everyone has been responding with pure kindness. One guy even travelled from another office to my office just to help me solve a problem because we could not do it online.&lt;/p&gt;
&lt;p&gt;Now the office. It is not without its flaws, but it is the single most impressive office building that I have ever seen. Full of greenery and flowers everywhere. Everything is designed to work and look good together. You have to visit it to feel its grandiose. I might be able to give you a tour.&lt;/p&gt;
&lt;p&gt;With that said, our team still needs more software engineers. If those above descriptions look great to you, please get in touch :)&lt;/p&gt;
&lt;h2&gt;The Valley&lt;/h2&gt;
&lt;p&gt;Let’s get the most significant negative out of the way: the price of housing. It is currently taking a large chunk of my after-tax income. Renting a small two bedroom apartment costs double the amount I paid for my large four bedroom house mortgage in Aussie. Silicon Valley is indeed one of the most expensive places to live in the world.&lt;/p&gt;
&lt;p&gt;Aside from the housing price, life is pretty good here. You will get access to Amazon Prime, which doesn’t sound like much, but the two-day free shipping is light years ahead of Aussie. I got a gigabit internet just because I can, with the same price I paid for &lt;code&gt;100/40&lt;/code&gt; in Aussie. The food is generally around the same price as Aussie, but with more substantial portions. Moreover, there are always unlimited drinks at any fast food restaurants.&lt;/p&gt;
&lt;p&gt;Car price is low here compared to Aussie. People drive pretty badly. They rarely use the turn signal correctly. They ignore the speed limit and driving 15mph above the legal limit is a daily occurrence.&lt;/p&gt;
&lt;p&gt;Driving on the wrong side of the road was disorienting at first, but you get used to it in a week or so. The unit conversion will take longer. Converting km to miles are approximately one and half times. Celsius to Fahrenheit does not make sense because they are not linear. Your best bet is to use certain milestones e.g. &lt;code&gt;70f == 21c, 60f == 16c, 50f == 10c&lt;/code&gt; and estimate from there.&lt;/p&gt;
&lt;p&gt;My wife and baby seem to like this place a lot too. All the libraries here have baby classes where parents can take their babies in for story time, music class and art classes for free. My wife has been making friends with other moms this way.&lt;/p&gt;
&lt;p&gt;One of the most problematic things is the concept of Credit Score. Since we just moved here, we do not have any credit history. Because of that, getting a credit card or car loan is tough. The only way I could get those now is because I could show them the offer letter from my company, which apparently helps a lot. It will be a couple of years before I could get good enough history to get a mortgage.&lt;/p&gt;
&lt;h2&gt;The future&lt;/h2&gt;
&lt;p&gt;Personally, I would love to stay here if things keep going well like this. There are still many things to try and places to explore.&lt;/p&gt;
&lt;p&gt;The first four months here just makes me realise that the Aussie tech scene could improve substantially.&lt;/p&gt;
&lt;p&gt;I think this is a perfect place to raise kids because they would grow up in a great environment, surrounded by kids from smart families.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Join me!&lt;/strong&gt;&lt;/p&gt;
</content:encoded></item><item><title>Wisdom of Insecurity - Book Review &amp; Summary</title><link>https://www.augusteo.com/blog/wisdom-of-insecurity/</link><guid isPermaLink="true">https://www.augusteo.com/blog/wisdom-of-insecurity/</guid><description>Wisdom of Insecurity - Book Review &amp; Summary - a blog post by Victor Augusteo</description><pubDate>Tue, 10 Apr 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/wisdom-of-insecurity/wisdom-of-insecurity-51a8jnCcMoL.jpg&quot; alt=&quot;wisdom-of-insecurity-51a8jnCcMoL.jpg&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Review&lt;/h2&gt;
&lt;p&gt;This book is full of pseudo-mysticism and pernicious nostalgia. It glorifies eastern philosophy at the expense of enlightenment ideology that brought the world all the technological advancement that we enjoy today.&lt;/p&gt;
&lt;p&gt;The central thesis of the book that humanity is less happy now that we have strayed from religion and trapped by consumerism. However, is it true?&lt;/p&gt;
&lt;p&gt;Steven Pinker’s Enlightenment book comes with 72 charts of human civilisation advancement. Chief among them is happiness and life satisfaction data:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/wisdom-of-insecurity/wisdom-of-insecurity-9b4a9188b9aada891db43b8ec5600f.jpg&quot; alt=&quot;wisdom-of-insecurity-9b4a9188b9aada891db43b8ec5600f.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;source: &lt;a href=&quot;https://ourworldindata.org/happiness-and-life-satisfaction&quot;&gt;https://ourworldindata.org/happiness-and-life-satisfaction&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If the research is valid, it proves that we are not descending into atheism hell. Instead, we are getting happier.&lt;/p&gt;
&lt;p&gt;Sure, the absolute number of people who are depressed and having a hard time finding meaning in life might be increasing, but we have to look at it in context. The world population has exploded significantly over the past several decades due to advance in medicine, so the absolute number of almost all demographic has increased with it.&lt;/p&gt;
&lt;p&gt;The next big problem is how people deal with lack of faith in religion. The book postulates that modern people try to fill the void with stuff. Hedonic treadmill is a well-studied phenomenon, but it is not an exclusively modern one. The poor religious peasant in medieval Europe would desire an ever larger homestead and farmland. Once they have gotten that, they would be happy for a while before adapting to the new home, then repeat the process all over again. Their lack of ability to get rich is the great deterrent to hedonism.&lt;/p&gt;
&lt;p&gt;The modern problem is that since we have more economic mobility and much wealthier compared to the peasant, people could afford larger homes and faster car. Because of this ability, people would spend a more substantial portion of their resources on the hedonic treadmill.&lt;/p&gt;
&lt;p&gt;Instead of going back to the old eastern religion and philosophy, humanity could look forward to a different belief system like humanism. We could look at Star Trek and see what can we do to achieve interstellar travel. Let the past go. We have been using religion as a crutch for thousands of years without much to show for it.&lt;/p&gt;
&lt;p&gt;In conclusion, this book is not malicious. It is just operating under false assumptions and doesn’t try to put things into modern context. We can still learn something from this book.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rating: 3 out of 5&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;h3&gt;What is the meaning of life?&lt;/h3&gt;
&lt;p&gt;Religion offers strict doctrines that govern how people live their life. People feel more fulfilled when they follow the rules. Do good stuff according to the book, and one will live forever in heaven. The eternal afterlife is a very useful carrot and stick.&lt;/p&gt;
&lt;p&gt;During the industrial revolution and rapid technological advancement, the world seems to move away from mysticism. Many people lost their sense of direction. When they lose faith in religion, they lose their compass in life. Why do we have to suffer in this life if there isn’t any afterlife to look forward?&lt;/p&gt;
&lt;p&gt;The lost souls attempt to fill the hole with drugs, excessive partying or to become workaholics. The more they try to stimulate themselves, the more stimulation they need to feel alive. They have become desensitised. Thus the cycle of addiction continues.&lt;/p&gt;
&lt;h3&gt;Consumerism&lt;/h3&gt;
&lt;p&gt;We are not here in this world to buy and consume stuff. New car and new clothes are fun for a while, but they do not give life much meaning.&lt;/p&gt;
&lt;p&gt;Without religion to guide them, people fell into hedonic treadmill where they always want to get the bigger house and the faster car. It is a treadmill for a reason. The thing they chase is designed never to get caught. We are playing a rigged game.&lt;/p&gt;
&lt;p&gt;We train our children at an early age to follow the rules. Study hard to get good grades. Get good grades to go to college. Go to college to get a good job. Get a good job to make lots of money. Make lots of money to can buy stuff. Buy more stuff to be happy.&lt;/p&gt;
&lt;p&gt;What happens when they are still unhappy at the end?&lt;/p&gt;
&lt;p&gt;What if a child wanted to become a nurse to help others, but everyone around them steers them to go to medical school?&lt;/p&gt;
&lt;h3&gt;Pleasure &amp;amp; Pain&lt;/h3&gt;
&lt;p&gt;We want the sweet feelings without the bad. We want the joys of life but not the pain. These two are interconnected, and it all depends on perspective. We need to feel the lowest low to appreciate the highest high.&lt;/p&gt;
&lt;p&gt;Just like Passenger sang on his song ‘Let her go.’&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Well, you only need the light when it’s burning low Only miss the sun when it starts to snow Only know you love her when you let her go Only know you’ve been high when you’re feeling low Only hate the road when you’re missing home Only know you love her when you let her go And you let her go&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Learn to view all events as temporary. Neither good nor bad. In Zen Buddhism, letting go of everything indeed is the path to enlightenment.&lt;/p&gt;
&lt;p&gt;Let go of the past, embrace the present. Don’t worry about how the future is going to impact the present, just enjoy the now.&lt;/p&gt;
&lt;p&gt;Sometimes thinking hard about a problem will bring you no closer to the solution. Instead, you should go for a walk in the park, or take a long hot shower. Ideas to solve your problem might emerge serendipitously. Inspiration comes when we aren’t thinking hard about it.&lt;/p&gt;
</content:encoded></item><item><title>Grit - Book Review &amp; Summary</title><link>https://www.augusteo.com/blog/grit/</link><guid isPermaLink="true">https://www.augusteo.com/blog/grit/</guid><description>Grit - Book Review &amp; Summary - a blog post by Victor Augusteo</description><pubDate>Sat, 07 Apr 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/grit/grit-083D795F-82F7-48EA-A574-287AD6CDC90C.png&quot; alt=&quot;grit-083D795F-82F7-48EA-A574-287AD6CDC90C.png&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Review&lt;/h2&gt;
&lt;p&gt;Grit is an excellent book with useful and actionable messages about what makes people successful. The primary hypotheses of the book centres around the concept of grittiness, which is the willingness to power through a challenging situation. The higher your grit score is, the more likely that you would succeed in your endeavour.&lt;/p&gt;
&lt;p&gt;This idea is a modern rehash of the classic stoicism principles with a massive amount of anecdotes. If you are familiar with Seneca and Marcus Aurelius, this book has little to offer you aside from the modern context.&lt;/p&gt;
&lt;p&gt;The redeeming quality of the book is the stuff other than just the concept of grit, where we would learn what kind of other attributes that make someone grittier than others.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rating: 4 stars out of 5 stars&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;In general, people agree that effort is more important than raw talent. However, when given a choice to recruit either a talented or determined candidate, they would overwhelmingly choose the talented candidate, which seems to show that we have an innate preference towards the talented.&lt;/p&gt;
&lt;h3&gt;Effort&lt;/h3&gt;
&lt;p&gt;The “Effort” is a multiplier for both skill and talent, which means you would gain greater benefit when you increase your effort. To me, this doesn’t mean much if we agree that innate talent is a constant, and skill is a derivative of talent. The only variable in the equation is the effort, so it is the only one you can influence.&lt;/p&gt;
&lt;p&gt;A super talented athlete who trains with low effort might get beaten by mildly talented one who works hard. However if the former trains with higher energy, she might be unbeatable by the latter.&lt;/p&gt;
&lt;p&gt;The moral is that since talent is static, just don’t worry about it. If you put high enough effort, you would be good at something. The problem is that talent puts a ceiling for your accomplishment. If you are ok with not being world class, feel free to put effort into an activity that you aren’t talented at. For me, it is writing ;)&lt;/p&gt;
&lt;h3&gt;Sticking with it&lt;/h3&gt;
&lt;p&gt;Consistency is key. Doing small thing frequently is much better than doing a big thing once in a while.&lt;/p&gt;
&lt;p&gt;A big goal of becoming a doctor is too high-level to visualise easily. Break down the goal into mid-level goals, e.g. pass the exam, and then to low-level goal, e.g. study chapter 12-15.&lt;/p&gt;
&lt;p&gt;You will much more likely to stick with your plan if you love what you do, thus doing something you like is the key. On a poll, only 13 percent of US workers feel engaged with their work. With that attitude, it would be tough to improve consistently, which is good news because this means you have 87 percent chance to outperform your coworkers for the promotion if you love your job.&lt;/p&gt;
&lt;p&gt;People have more than one thing they love. You don’t have to search for the ‘one true thing’ to love. Just something good enough would do. The quest for searching ‘one true thing’ follows a diminishing return curve.&lt;/p&gt;
&lt;p&gt;One great predictor of ‘one true thing’ is that if you can think the calling to benefit other people. Jobs that is low pay, but serves the community tends to have much higher satisfaction, e.g. school teacher and zookeeper. These jobs give a greater sense of purpose in life and belief that they are making the world a better place.&lt;/p&gt;
&lt;p&gt;If you are not a zookeeper, don’t despair. Even within the same job, different people would find a different meaning.&lt;/p&gt;
&lt;h3&gt;Practice&lt;/h3&gt;
&lt;p&gt;Now that you found the thing that you love, and you are having fun doing it, how do you improve?&lt;/p&gt;
&lt;p&gt;There is a famous saying in software engineering field:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;you could have ten years experience, or 1 year of experience repeated ten times.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The most significant difference is ‘Deliberate Practice’ where you go over the things you suck at and keep doing it until you improve. Malcolm Gladwell’s Outlier explored this concept with the 10,000 hours deliberate practice rule.&lt;/p&gt;
&lt;p&gt;One thing you need to do to practice deliberately is to keep track of progress. If you are lifting weight, write down your reps and sets. Make charts to see your progression. If there is a particular lift that you aren’t progressing, change your routine so that you are doing extra volume on the muscle group.&lt;/p&gt;
&lt;p&gt;Having a master of the craft to give you constant feedback is critical. By yourself, you won’t be able to notice slight mistakes that get repeated over an extended period. A master would see that and help you correct your course.&lt;/p&gt;
&lt;h3&gt;How to raise your kids to be gritty&lt;/h3&gt;
&lt;p&gt;Since talent is static, never tell your kids that they are brilliant. Smart kids who don’t know the value of hard work tend not to endure when things get hard.&lt;/p&gt;
&lt;p&gt;Instead of saying, “You’re a natural, I love that,” you should tell your kids, “You’re a learner, I love that!”&lt;/p&gt;
&lt;p&gt;Immersing yourself in a gritty culture helps you to be grittier. Lets say your personal running record is 5 miles, if you run with a group of friends who usually runs 8 miles, you would be working really hard to keep up and finish the 8 miles.&lt;/p&gt;
&lt;p&gt;Set examples of a gritty culture for your kids. Make it a rule for the entire family to self-select one ‘hard thing’ to stick with for a determined period. Let the kids select it, and your job is to help them keep doing it until the time is up and to make sure they don’t quit.&lt;/p&gt;
&lt;p&gt;If little Jane selected Basketball but wanted to quit after two weeks, kindly let her know that she can only stop after finishing the season. If little Sophia learns ballet, she has to stick with it until the scheduled talent show.&lt;/p&gt;
&lt;p&gt;Of course, the parents need to set a good example and show grit, or it will all be for naught.&lt;/p&gt;
</content:encoded></item><item><title>Using Goaccess to Replace Google Analytics feat. Caddy and Docker</title><link>https://www.augusteo.com/blog/goaccess-replace-analytics/</link><guid isPermaLink="true">https://www.augusteo.com/blog/goaccess-replace-analytics/</guid><description>Using Goaccess to Replace Google Analytics feat. Caddy and Docker - a blog post by Victor Augusteo</description><pubDate>Thu, 05 Apr 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://goaccess.io/&quot;&gt;Goaccess&lt;/a&gt; is a log analyser that can be used to visualise your website traffic.&lt;/p&gt;
&lt;h2&gt;Why use it?&lt;/h2&gt;
&lt;p&gt;It has three advantages compared to traditional web analytics like Google Analytics or Piwik.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Goaccess analyses the web server logs directly, so it doesn’t need to load javascript file. This will speed up the page load significantly.&lt;/li&gt;
&lt;li&gt;All the data is stored in your server, not on Google’s server. You own it.&lt;/li&gt;
&lt;li&gt;You can access it either using CLI or opening the generated html file.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Disadvantages&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Harder to setup; you can’t just copy paste the javascript loading codes to &lt;code&gt;&amp;lt;HEAD&amp;gt;&lt;/code&gt; section.&lt;/li&gt;
&lt;li&gt;Very basic tracking mechanism. You can’t set customer sales funnel or drill down on granular data.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For my use case, where I just wanted to know the unique visitors number, referrer, etc. It is good enough compromise.&lt;/p&gt;
&lt;h2&gt;Dockerizing Goaccess with Caddy&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://hub.docker.com/r/allinurl/goaccess/&quot;&gt;official Docker image&lt;/a&gt; contains good tutorial on how to use it.&lt;/p&gt;
&lt;p&gt;The first step is to either build and run it, or just to use &lt;code&gt;docker-compose&lt;/code&gt;. Any self-respecting Docker user will use the compose, so let’s take a look.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;docker-compose.yml&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: &apos;2&apos;
services:
     caddy:
		   # other stuff
         volumes:
             - ./docker/caddy/logs:/etc/logs &amp;lt;- IMPORTANT
             - ./docker/goaccess/.html:/site/goaccess &amp;lt;- To view it on subdomain
     goaccess:
         image: allinurl/goaccess
         restart: unless-stopped
         volumes:
             - ./docker/goaccess/data:/srv/data
             - ./docker/goaccess/.html:/srv/report
             - ./docker/caddy/logs:/srv/logs

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;./docker/caddy/logs:/etc/logs&lt;/code&gt; This will share save the Caddy server logs to system disk folder, which in turn will be shared and read by Goaccess Docker image.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;./docker/goaccess/.html:/site/goaccess&lt;/code&gt; This will share let Caddy server to serve the generated Goaccess html file on a subdomain&lt;/p&gt;
&lt;p&gt;The Goaccess compose script is very standard, just following the default config on the Docker hub page.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Caddyfile&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;in Caddyfile, we just need to add this line to your desired host server to save the generated logs to disk:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;log / /etc/logs/requests.log &quot;{combined}&quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This means we are logging all request (&lt;code&gt;/&lt;/code&gt;) into &lt;code&gt;/etc/logs/requests.log&lt;/code&gt; using the &lt;code&gt;&quot;{combined}&quot;&lt;/code&gt; format. Combined is used here because it contains referrer and user agent.&lt;/p&gt;
&lt;p&gt;You can read more information on the log syntax on the &lt;a href=&quot;https://caddyserver.com/docs/log&quot;&gt;official docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you wanted to access the generated html file on a subdomain, just add this to the Caddyfile:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; www.goaccess.example.com {
     basicauth / admin password
     root /site/goaccess
     gzip
 }

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will serve the generated &lt;code&gt;index.html&lt;/code&gt; on the subdomain. Basic auth is important here if you don’t want just anyone to be able to see your analytics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;goaccess.conf&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We need to specify the config file for Goaccess to tell it which file to load, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; log-format COMBINED
 log-file /srv/logs/requests.log
 output /srv/report/index.html
 real-time-html true

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;COMBINED&lt;/code&gt; is used here because we tell Caddy server to generate &lt;code&gt;Combined&lt;/code&gt; log format.&lt;/p&gt;
&lt;p&gt;You are done, just run &lt;code&gt;docker-compose up -d&lt;/code&gt; to spin up Caddy and Goaccess.&lt;/p&gt;
&lt;p&gt;When you access &lt;code&gt;www.goaccess.example.com&lt;/code&gt; you should see something like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/goaccess-replace-analytics/goaccess-replace-analytics-97a4ab11540ca4adebb1619ea8381bc2.jpg&quot; alt=&quot;goaccess-replace-analytics-97a4ab11540ca4adebb1619ea8381bc2.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I hope this helps!&lt;/p&gt;
</content:encoded></item><item><title>The Wonders of Caddyfile</title><link>https://www.augusteo.com/blog/caddyfile/</link><guid isPermaLink="true">https://www.augusteo.com/blog/caddyfile/</guid><description>The Wonders of Caddyfile - a blog post by Victor Augusteo</description><pubDate>Mon, 01 Jan 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;It is incredibly easy to do a lot of things with Caddy server. We will explore reverse proxy, wildcard HTTPS cert and catch-all www redirection. Then we will see how to combine them together to do powerful stuff in one short Caddyfile.&lt;/p&gt;
&lt;h2&gt;Reverse Proxy&lt;/h2&gt;
&lt;p&gt;To reverse proxy a subdomain to docker container, all you need is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; www.subdomain.augusteo.com {
     proxy / wordpress:8888 {
         transparent
     }
 }

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this, the subdomain will point to the &lt;code&gt;wordpress&lt;/code&gt; container’s port 8888. If you aren’t using docker and just running things on localhost, you could replace that with &lt;code&gt;localhost:3000&lt;/code&gt; or whatever port you are running the app from.&lt;/p&gt;
&lt;h2&gt;Wildcard HTTPS&lt;/h2&gt;
&lt;p&gt;I’m hosting my blog on &lt;a href=&quot;https://www.vultr.com/?ref=6932571&quot;&gt;Vultr&lt;/a&gt; and uses its DNS, so I could use it for wildcard cert.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hugo.augusteo.com {
     root /site/augusteocom
     gzip
	   tls {
        dns vultr
     }
 }

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This config will serve &lt;code&gt;augusteo.com&lt;/code&gt; from the root path and initialise wildcard HTTP TLS using the Vultr DNS. To enable this, you need to either:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;download caddy binary with Vultr plugin&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Redirect all non-www to www&lt;/h2&gt;
&lt;p&gt;Its very easy to redirect one domain with the &lt;code&gt;redir&lt;/code&gt; keyword, but its a bit tricky to do it for all subdomain at once:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; http://*.augusteo.com, https://*.augusteo.com {
     redir {
         if {host} not_starts_with www
         / https://www.{label1}.augusteo.com{uri}
     }
     gzip
     tls {
         dns vultr
     }
 }

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We could use the &lt;code&gt;not_starts_with&lt;/code&gt; function to check if non-www domain is being requested, then do 301 redirection to www version of it. We can also specify the &lt;code&gt;tls&lt;/code&gt; here so all the subdomain get encrypted with the wildcard.&lt;/p&gt;
&lt;h2&gt;Combining them all&lt;/h2&gt;
&lt;p&gt;Now this is the current Caddyfile that I’m using on this server:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; # Main domain
 http://augusteo.com, https://augusteo.com {
     redir {
         if {host} not_starts_with www
         / https://hugo.augusteo.com{uri}
     }
     tls {
         dns vultr
     }
 }

 hugo.augusteo.com {
     root /site/augusteocom
     gzip
 }

 # Subdomains
 http://*.augusteo.com, https://*.augusteo.com {
     redir {
         if {host} not_starts_with www
         / https://www.{label1}.augusteo.com{uri}
     }
     gzip
     tls {
         dns vultr
     }
 }

 www.subdomain.augusteo.com {
     proxy / wordpress:8888 {
         transparent
     }
 }

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This file would:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;redirect all non-www to www page. We had to do it twice because the subdomain &lt;code&gt;.&lt;/code&gt; doesn’t cover the main domain.&lt;/li&gt;
&lt;li&gt;reverse proxy the &lt;code&gt;subdomain&lt;/code&gt; to docker container&lt;/li&gt;
&lt;li&gt;use wildcard HTTP TLS cert on all domains.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To add new subdomain, we would just need to add the reverse proxy or the root path of it, e.g.:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; www.subdomain2.augusteo.com {
     proxy / rubyonrails:3000 {
         transparent
     }
 }

 www.subdomain3.augusteo.com {
     root /site/jekyllstatic
 }

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running Caddy server extremely simple compared to running Nginx and a lot faster compared to Apache. Learn it and love it.&lt;/p&gt;
</content:encoded></item><item><title>Summary of Fooled by Randomness</title><link>https://www.augusteo.com/blog/fooled-by-randomness/</link><guid isPermaLink="true">https://www.augusteo.com/blog/fooled-by-randomness/</guid><description>Summary of Fooled by Randomness - a blog post by Victor Augusteo</description><pubDate>Mon, 01 Jan 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/fooled-by-randomness/fooled-by-randomness-C702FC59-2882-4DAC-BFCA-B38057BE8D09.png&quot; alt=&quot;fooled-by-randomness-C702FC59-2882-4DAC-BFCA-B38057BE8D09.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/0812975219/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0812975219&amp;amp;linkCode=as2&amp;amp;tag=augusteo-20&amp;amp;linkId=e3733659968d6016596be9a5700cc6c1&quot;&gt;Fooled by Randomness: The Hidden Role of Chance in Life&lt;/a&gt; and in the Markets by Nassim Nicholas Taleb&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;There are professions with low randomness (e.g. dentist) that can only be successful with skill; and professions with high randomness (e.g. day trader) that could see someone unskilled get successful purely based on luck and variance.&lt;/li&gt;
&lt;li&gt;People often mistake luck and variance for skill, i.e. survivor-ship bias.&lt;/li&gt;
&lt;li&gt;The absence of evidence is not evidence of absence. Just because we have only encountered white swans so far, doesn’t mean that all swans are white. It only takes one black swan to prove the hypotheses wrong.&lt;/li&gt;
&lt;li&gt;The scientific method can only assess falsifiable hypotheses. Good hypotheses can be proven wrong quickly, like by a single black swan.&lt;/li&gt;
&lt;li&gt;Theory of evolution postulates survival of the fittest, not the best. Sometimes things that survive the longest is the sub-optimal choice, e.g. QWERTY keyboard.&lt;/li&gt;
&lt;li&gt;Nature and life are non-linear. We can build a sandcastle with a million grains of sand, yet adding another would bring the whole thing down.&lt;/li&gt;
&lt;li&gt;The human mind is not evolved to handle sophisticated statistics and randomness. We depend on heuristics, e.g. availability heuristics where we value things that are easily visible more.&lt;/li&gt;
&lt;li&gt;The heuristics cause us to attribute the wrong thing as a cause. e.g. our success is caused by our skills, but failure is bad luck&lt;/li&gt;
&lt;li&gt;We are driven by emotion which helps us to make a quick decision, even though it is sub-optimal. Emotion influences our rational thinking rather than the other way around.&lt;/li&gt;
&lt;li&gt;It is a lot easier to find ways to avoid triggering emotion rather than try to fight it; e.g. checking share portfolio once a month instead of daily to avoid impulse sale.&lt;/li&gt;
&lt;li&gt;Hindsight bias is another big one that we need to be aware. Past events always seem predictable and easier to handle than they were at the time. We naturally find patterns where there were none.&lt;/li&gt;
&lt;li&gt;Overfitting is when we try to find pattern, rule or formula to fit historical data too much that it becomes less able to predict future events.&lt;/li&gt;
&lt;li&gt;We need to account for an unexpected event (black swan event). e.g. stock market crash and climate change.&lt;/li&gt;
&lt;li&gt;Look for both chance and magnitude of an event. e.g. 99.9% chance of winning $1 with 0.1% chance of losing $10,000 looks good on paper, but the expected outcome is a loss of ~$9.&lt;/li&gt;
&lt;li&gt;Too often we only looked at the chance and not enough of the magnitude. Rare events could be catastrophic.&lt;/li&gt;
&lt;li&gt;Embrace beautiful randomness, e.g. poetry, art.&lt;/li&gt;
&lt;li&gt;Stoicism philosophy is a great way to deal with randomness. We do not control randomness, but we manage our reaction to it. Expect it, don’t complain or blame. Face variance with dignity and wisdom.&lt;/li&gt;
&lt;li&gt;Reading daily news takes a lot of effort for little reward. Therefore we should stop doing it. If the news is essential, it will travel through the grapevine to reach you somehow.&lt;/li&gt;
&lt;li&gt;Same goes for checking portfolio every minute. We might be affected by small variance and let our emotion take over.&lt;/li&gt;
&lt;li&gt;Learn the Monte Carlo simulation. I found this &lt;a href=&quot;https://www.portfoliovisualizer.com/monte-carlo-simulation#analysisResults&quot;&gt;great website&lt;/a&gt; to visualize portfolio return. &lt;a href=&quot;https://www.portfoliovisualizer.com/examples&quot;&gt;The website&lt;/a&gt; also have plenty of examples to play around.&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>Lake Tahoe Winter</title><link>https://www.augusteo.com/blog/lake-tahoe-winter/</link><guid isPermaLink="true">https://www.augusteo.com/blog/lake-tahoe-winter/</guid><description>Lake Tahoe Winter - a blog post by Victor Augusteo</description><pubDate>Mon, 01 Jan 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-2a7712aee623e81ddc03bbb183488669-thumb.jpg&quot; alt=&quot;lake-tahoe-winter-2a7712aee623e81ddc03bbb183488669-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![lake-tahoe-winter-2a7712aee623e81ddc03bbb183488669-thumb 1.jpg](https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-2a7712aee623e81ddc03bbb183488669-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-3aec308ca999f7a169dcfad468694256-thumb.jpg&quot; alt=&quot;lake-tahoe-winter-3aec308ca999f7a169dcfad468694256-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![lake-tahoe-winter-3aec308ca999f7a169dcfad468694256-thumb 1.jpg](https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-3aec308ca999f7a169dcfad468694256-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-411df3bdc4ccc5001654363881974b98-thumb.jpg&quot; alt=&quot;lake-tahoe-winter-411df3bdc4ccc5001654363881974b98-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![lake-tahoe-winter-411df3bdc4ccc5001654363881974b98-thumb 1.jpg](https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-411df3bdc4ccc5001654363881974b98-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-454a84bedb3b98841ceb05bca244f0e5-thumb.jpg&quot; alt=&quot;lake-tahoe-winter-454a84bedb3b98841ceb05bca244f0e5-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![lake-tahoe-winter-454a84bedb3b98841ceb05bca244f0e5-thumb 1.jpg](https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-454a84bedb3b98841ceb05bca244f0e5-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-4ea602f06bfc24c2adcd8e50d9dc794f-thumb.jpg&quot; alt=&quot;lake-tahoe-winter-4ea602f06bfc24c2adcd8e50d9dc794f-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![lake-tahoe-winter-4ea602f06bfc24c2adcd8e50d9dc794f-thumb 1.jpg](https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-4ea602f06bfc24c2adcd8e50d9dc794f-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-6478c494f8ee2d0090c8d04f24cb1f97-thumb.jpg&quot; alt=&quot;lake-tahoe-winter-6478c494f8ee2d0090c8d04f24cb1f97-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![lake-tahoe-winter-6478c494f8ee2d0090c8d04f24cb1f97-thumb 1.jpg](https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-6478c494f8ee2d0090c8d04f24cb1f97-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-8092907e6fed2bd9470f7fb20fdc3610-thumb.jpg&quot; alt=&quot;lake-tahoe-winter-8092907e6fed2bd9470f7fb20fdc3610-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![lake-tahoe-winter-8092907e6fed2bd9470f7fb20fdc3610-thumb 1.jpg](https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-8092907e6fed2bd9470f7fb20fdc3610-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-8a8ee1fca2d0b8fc9c1c144ac97b7697-thumb.jpg&quot; alt=&quot;lake-tahoe-winter-8a8ee1fca2d0b8fc9c1c144ac97b7697-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![lake-tahoe-winter-8a8ee1fca2d0b8fc9c1c144ac97b7697-thumb 1.jpg](https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-8a8ee1fca2d0b8fc9c1c144ac97b7697-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-a94d4a3160037a5ebafc4575711b4fa4-thumb.jpg&quot; alt=&quot;lake-tahoe-winter-a94d4a3160037a5ebafc4575711b4fa4-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![lake-tahoe-winter-a94d4a3160037a5ebafc4575711b4fa4-thumb 1.jpg](https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-a94d4a3160037a5ebafc4575711b4fa4-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-cd5b47e5b8ee42ae6cfbe089f83f021a-thumb.jpg&quot; alt=&quot;lake-tahoe-winter-cd5b47e5b8ee42ae6cfbe089f83f021a-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![lake-tahoe-winter-cd5b47e5b8ee42ae6cfbe089f83f021a-thumb 1.jpg](https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-cd5b47e5b8ee42ae6cfbe089f83f021a-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-e03a3318af598b49c2c0af0e9b37c869-thumb.jpg&quot; alt=&quot;lake-tahoe-winter-e03a3318af598b49c2c0af0e9b37c869-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![lake-tahoe-winter-e03a3318af598b49c2c0af0e9b37c869-thumb 1.jpg](https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-e03a3318af598b49c2c0af0e9b37c869-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-ec1592f7680f0932ab4ecd90ac399426-thumb.jpg&quot; alt=&quot;lake-tahoe-winter-ec1592f7680f0932ab4ecd90ac399426-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![lake-tahoe-winter-ec1592f7680f0932ab4ecd90ac399426-thumb 1.jpg](https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-ec1592f7680f0932ab4ecd90ac399426-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-fde7a6448c1aa892b5773843511b06c0-thumb.jpg&quot; alt=&quot;lake-tahoe-winter-fde7a6448c1aa892b5773843511b06c0-thumb.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;![lake-tahoe-winter-fde7a6448c1aa892b5773843511b06c0-thumb 1.jpg](https://www.augusteo.com/assets/blog/lake-tahoe-winter/lake-tahoe-winter-fde7a6448c1aa892b5773843511b06c0-thumb 1.jpg)&lt;/p&gt;
&lt;p&gt;License:&lt;/p&gt;
</content:encoded></item><item><title>Running Gitlab CI With Docker</title><link>https://www.augusteo.com/blog/gitlab-ci-docker/</link><guid isPermaLink="true">https://www.augusteo.com/blog/gitlab-ci-docker/</guid><description>Running Gitlab CI With Docker - a blog post by Victor Augusteo</description><pubDate>Sat, 18 Mar 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently I had the opportunity to play with Gitlab CI and had quite a bit of fun on the process. I wanted to share my experience in a tutorial format to share the fun with you :)&lt;/p&gt;
&lt;p&gt;This tutorial assumes no prior knowledge of Docker, but you need to know what CI is and how SSH works.&lt;/p&gt;
&lt;h3&gt;Summary:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Setup &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file to tell the CI what to do&lt;/li&gt;
&lt;li&gt;Setup Gitlab CI with docker&lt;/li&gt;
&lt;li&gt;Create your Docker image and put up on Docker Hub&lt;/li&gt;
&lt;li&gt;Use the newly created Docker image to speed up CI cycle&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Background&lt;/h3&gt;
&lt;p&gt;Our purpose is simple, and we want every single commit on dev branch to be rsync-ed to the server with an rsa ssh key. We are dealing with static files, so rsync is enough.&lt;/p&gt;
&lt;p&gt;Now Gitlab offers a built in CI, but we still need to set up its runner, instructions and the docker images. Let’s get to it.&lt;/p&gt;
&lt;h1&gt;Setting up Gitlab CI&lt;/h1&gt;
&lt;h3&gt;Config file&lt;/h3&gt;
&lt;p&gt;If you haven’t run any CI in Gitlab before, you need to set up the instructions first. To get started, just open up your project on Gitlab and click on &lt;code&gt;Set up CI&lt;/code&gt; button.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/gitlab-ci-docker/gitlab-ci-docker-F7E6DFFD-92BA-4B7B-AC58-4BEA60FF4BB4.png&quot; alt=&quot;gitlab-ci-docker-F7E6DFFD-92BA-4B7B-AC58-4BEA60FF4BB4.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;￼ It will bring up a new file screen with the name &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; pre-filled. Type this into it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;image: centos:latest

before_script:
  - yum install -y which
  - &apos;which ssh-agent || ( yum install -y openssh openssh-server openssh-clients openssl-libs )&apos;
  - &apos;which rsync || ( yum install -y rsync )&apos;
  - eval $(ssh-agent -s)
  - ssh-add &amp;lt;(echo &quot;$SSH_PRIVATE_KEY&quot;)
  - mkdir -p ~/.ssh
  - echo -e &quot;Host *\n\tStrictHostKeyChecking no\n\n&quot; &amp;gt; ~/.ssh/config

staging-deployment:
  stage: deploy
  script:
    - rsync -hrvz files/* root@yourhost.com:/var/www/html/
  only:
    - dev

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This file might not make sense now, but I promise by the end of this tutorial, you will understand it. Let’s go over them.&lt;/p&gt;
&lt;p&gt;Line 1 tells the CI runner to fetch the Docker image &lt;code&gt;centos:latest&lt;/code&gt; from &lt;a href=&quot;https://hub.docker.com/_/centos/&quot;&gt;Docker Hub&lt;/a&gt; . If you aren’t familiar with Docker, just think of it as a container of all the stuff you need to do a particular task. Please have a read the &lt;a href=&quot;https://www.docker.com/what-docker&quot;&gt;What is Docker&lt;/a&gt; page, but it might raise more question than it answers. We will explore it more later.&lt;/p&gt;
&lt;p&gt;Any commands inside before_script will be executed before every CI cycle, so this is a good place to set up our SSH and dependencies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Line 4–6&lt;/strong&gt; will install which, openssh and rsync to run our CI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Line 7–10&lt;/strong&gt; sets up the SSH key that will be used by rsync. We should never put a private key into a repo, so we will be using Gitlab’s Secret Variable to store it. It can be referenced using the $SSH_PRIVATE_KEY line. We will set it up soon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Line 12&lt;/strong&gt; sets up the name of the CI task. In this case we call it staging-deployment because we are deploying to staging environment. Then on the next line, we tell Gitlab that this is a deploy task.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Line 15&lt;/strong&gt; is the meat of the CI, which is the actual deployment. Rsync will use the ssh key that was set up previously and will start syncing the files from your repo to the yourhost.com server. The -hrvz modifier means:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/h/: human readable
/r/: recursive, so subfolders get synced too
/v/: verbose, so we can find out what went wrong from the logs
/z/: compressed while transferring, to speed up and use less bandwidth

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Line 17&lt;/strong&gt; tells Gitlab only to execute this task when a commit is pushed to devbranch and not others. This will enable us to setup CI tasks for other branches as well. For example deploying to production server when a commit is pushed to release branch. Alright now that we understand this file, lets hit Commit Changes.&lt;/p&gt;
&lt;h3&gt;Setting up secret SSH variable&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/gitlab-ci-docker/gitlab-ci-docker-1EAFED43-FFDE-423F-ACBD-CFFAF5DE114A.png&quot; alt=&quot;gitlab-ci-docker-1EAFED43-FFDE-423F-ACBD-CFFAF5DE114A.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;￼ Once you have committed the CI config, click Settings and CI/CD Pipelinesand scroll down until you see the Secret Variables. Type in &lt;code&gt;SSH_PRIVATE_KEY&lt;/code&gt; on the Key field and paste in your private key. You can easily copy the private key to clipboard using &lt;code&gt;cat ~/.ssh/id_rsa | pbcopy&lt;/code&gt;. Then save them.&lt;/p&gt;
&lt;p&gt;I am assuming you have already copied the public pair to the deployment server. If you haven’t, please follow this &lt;a href=&quot;https://docs.gitlab.com/ce/ssh/README.html&quot;&gt;Gitlab tutorial&lt;/a&gt; .&lt;/p&gt;
&lt;h3&gt;Setting up CI runner&lt;/h3&gt;
&lt;p&gt;Now that our CI setup is done, we need something that will actually run the commands. The cheapest way would be to spin up a small linux VPS. You can provision one for $2.50/month from &lt;a href=&quot;https://www.vultr.com/pricing/&quot;&gt;Vultr&lt;/a&gt; .&lt;/p&gt;
&lt;p&gt;Head over to the &lt;a href=&quot;https://docs.gitlab.com/runner/install/&quot;&gt;Gitlab runner page&lt;/a&gt; for installation instructions for each of your operating systems. Make sure you select docker as the executor and enter &lt;code&gt;centos:latest&lt;/code&gt; when asked for Docker image.&lt;/p&gt;
&lt;p&gt;After this is done, refresh your CI/CD page on Gitlab and you should see the runner shown. For example:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/gitlab-ci-docker/gitlab-ci-docker-39F358E7-D5D6-46EE-BC1B-E0E48D888355.png&quot; alt=&quot;gitlab-ci-docker-39F358E7-D5D6-46EE-BC1B-E0E48D888355.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;￼ If it doesn’t show up, try to read the &lt;a href=&quot;https://about.gitlab.com/gitlab-ci/#gitlab-runner&quot;&gt;runner documentation&lt;/a&gt; for help or ask around.&lt;/p&gt;
&lt;h3&gt;First CI task!&lt;/h3&gt;
&lt;p&gt;Now you have set up the runner and CI config, and you are ready! Since our config states that the runner will only be executed when a commit is pushed to dev branch, you should do that then.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cd test-gitlab-ci
$ mkdir files
$ cd files
$ vim index.html // and type in your html
$ cd ..
$ git checkout -b dev
$ git add .
$ git commit -m ‘initial index.html’
$ git push origin dev

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Gitlab CI should detect the commit and initiate the task. Head over to Pipeline -&amp;gt; Jobs to see the job being executed:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/gitlab-ci-docker/gitlab-ci-docker-A8F90394-305F-43A4-801F-FB2BAF1F7F36.png&quot; alt=&quot;gitlab-ci-docker-A8F90394-305F-43A4-801F-FB2BAF1F7F36.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;￼ Hopefully things run well, and you should see a job running. You can click on the job to see the details.&lt;/p&gt;
&lt;h3&gt;Turning on the Docker service&lt;/h3&gt;
&lt;p&gt;If the CI job fails and when you check the logs, you see something like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ERROR: Preparation failed: Get http://unix.sock/v1.18/version: dial unix /var/run/docker.sock: connect: no such file or directory*&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It means you have forgotten to turn on the docker service in the CI server. Just SSH into the runner machine and start the service. It should either be:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ sudo service docker start&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ sudo systemctl *start* docker&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Once the daemon is up, you can rerun the failed task again and it should pass.&lt;/p&gt;
&lt;h3&gt;What you have achieved so far&lt;/h3&gt;
&lt;p&gt;So up to this point, you have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set up Gitlab CI config file&lt;/li&gt;
&lt;li&gt;Set up Gitlab CI runner&lt;/li&gt;
&lt;li&gt;Run your first commit to dev branch&lt;/li&gt;
&lt;li&gt;Fix an error!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Things seems to be going well, but there’s something that we can improve very easily.&lt;/p&gt;
&lt;p&gt;If you see the running time of the CI, you should see that it runs for more than one minutes. That is because the vanilla CentOS box doesn’t come with rsync and openssh out of the box, so we need to install them. That process gets repeated every-time thus sucks a lot of time. We can easily fix this by creating your own Docker image of CentOS but with rsync and openssh preinstalled.&lt;/p&gt;
&lt;h3&gt;Creating your Docker image&lt;/h3&gt;
&lt;p&gt;First, lets install Docker. If you are on macOS, your can &lt;a href=&quot;https://docs.docker.com/docker-for-mac/install/&quot;&gt;download the Docker app&lt;/a&gt; to handle all that for you. If you, look on the left sidebar for your operating system.&lt;/p&gt;
&lt;p&gt;Then lets create a new folder and make a Dockerfile&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ mkdir my-docker
$ cd my-docker
$ vim Dockerfile

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Type in this, but change the MAINTAINER to your own name.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;FROM centos:latest
MAINTAINER Augusteo

RUN yum -y update; yum clean all
RUN yum -y install openssh openssh-server openssh-clients openssl-libs rsync which; yum clean all

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To build the Docker image, just type in&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ docker build -t my-docker .&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You should see it fetching the &lt;code&gt;centos:latest&lt;/code&gt; and then installing all our dependencies. So next time you run your CI using this new image, it will skip the installation and goes straight to the deployment. In my case it cuts the deploy time from more than one minute to less than twenty seconds.&lt;/p&gt;
&lt;h3&gt;Pushing to Docker Hub&lt;/h3&gt;
&lt;p&gt;To use your new image, we need to push it to Docker hub. The process is pretty straightforward, just follow this:&lt;/p&gt;
&lt;p&gt;If you want to skip all this work and wanted just to use a ready made image, you can use mine:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hub.docker.com/r/augusteo/centos-rsync-openssh/&quot;&gt;https://hub.docker.com/r/augusteo/centos-rsync-openssh/&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Changing the Gitlab CI config&lt;/h3&gt;
&lt;p&gt;Now we need to change the Gitlab CI config file to make use of the new Docker image and remove the dependencies installation. Edit your &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file to look like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;image: augusteo/centos-rsync-openssh

before_script:
  - eval $(ssh-agent -s)
  - ssh-add &amp;lt;(echo &quot;$SSH_PRIVATE_KEY&quot;)
  - mkdir -p ~/.ssh
  - echo -e &quot;Host *\n\tStrictHostKeyChecking no\n\n&quot; &amp;gt; ~/.ssh/config

staging-deployment:
  stage: deploy
  script:
    - rsync -hrvz files/* root@yourhost.com:/var/www/html/
  only:
    - dev

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see that we have changed the image to the new CentOS image with rsync and &lt;code&gt;openssh&lt;/code&gt;. If you have pushed your own image, feel free to change this to your own.&lt;/p&gt;
&lt;p&gt;In before_script, we have removed the three dependencies installation lines since we have already done it on container level. Just commit and push this file, and you should see that your CI execution time reduced to less than twenty seconds.&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;You have learned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Setup &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file to tell the CI what to do&lt;/li&gt;
&lt;li&gt;Setup Gitlab CI with docker&lt;/li&gt;
&lt;li&gt;Create your Docker image and put up on Docker Hub&lt;/li&gt;
&lt;li&gt;Use the newly created Docker image to speed up CI cycle&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;I hope you enjoyed this article and it helps you in your development life!&lt;/strong&gt;&lt;/p&gt;
</content:encoded></item><item><title>Fixing Slow Macos Finder Samba</title><link>https://www.augusteo.com/blog/fixing-macos-samba/</link><guid isPermaLink="true">https://www.augusteo.com/blog/fixing-macos-samba/</guid><description>Fixing Slow Macos Finder Samba - a blog post by Victor Augusteo</description><pubDate>Sat, 04 Mar 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As a bonus, this will also enable searching on Samba too.&lt;/p&gt;
&lt;h3&gt;Problem&lt;/h3&gt;
&lt;p&gt;I have a external HDD plugged into a Raspi which acts as a seed box of some kind. The files are served with Samba. The problem was:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Listing large amount of file in a directory takes ages.&lt;/li&gt;
&lt;li&gt;Search doesn’t work at all, it just got stuck at “Searching”.&lt;/li&gt;
&lt;li&gt;Transfer speed was very slow.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Solution&lt;/h3&gt;
&lt;p&gt;After much searching, the fix is quite simple actually.&lt;/p&gt;
&lt;p&gt;First, SSH into the server with &lt;code&gt;$ ssh serverIp&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Then edit the Samba config by typing &lt;code&gt;$ vim /etc/samba/smb.conf&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Once it is open add these lines to your particularly slow Samba shares:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vfs objects = fruit
fruit:aapl = yes
fruit:encoding = native
fruit:locking = none
fruit:metadata = stream
fruit:resource = file

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lastly restart the server with &lt;code&gt;$sudo service smbd restart&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Result&lt;/h3&gt;
&lt;p&gt;Now the listing large directory should be pretty fast, search should work as normal and transfer speed should be around 10x faster.&lt;/p&gt;
&lt;h3&gt;References&lt;/h3&gt;
</content:encoded></item><item><title>Xcode Layout Constraint Issue</title><link>https://www.augusteo.com/blog/xcode-layout-constraint/</link><guid isPermaLink="true">https://www.augusteo.com/blog/xcode-layout-constraint/</guid><description>Xcode Layout Constraint Issue - a blog post by Victor Augusteo</description><pubDate>Mon, 20 Feb 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My typical solution for non-repeating view controllers nowadays is by using vertical UIStackView that is embedded within UIScrollView.&lt;/p&gt;
&lt;p&gt;So the stack goes:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Main UIView -&amp;gt; UIScrollView -&amp;gt; UIStackView -&amp;gt; Views array&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/xcode-layout-constraint/xcode-layout-constraint-1CA5A1E1-B0B5-4F19-97EE-40B591532B0B.png&quot; alt=&quot;xcode-layout-constraint-1CA5A1E1-B0B5-4F19-97EE-40B591532B0B.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This stack is very easy to set up in storyboard or code and constrained with auto layout.&lt;/p&gt;
&lt;p&gt;Naturally, the four sides of the scroll view will be constrained to the main UIView. The easiest way would be to use the tie-fighter icon. Notice that the top and bottom is glued to the layout guide instead of superview by default.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/xcode-layout-constraint/xcode-layout-constraint-D49F46DD-7203-4F72-9B2F-056F280B847B.png&quot; alt=&quot;xcode-layout-constraint-D49F46DD-7203-4F72-9B2F-056F280B847B.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The problem arises when we embed this view controller inside a navigation controller, thus creating a navigation item bar. This action will then push the stack view (inside the scroll view) down 64 pixels, seemingly without reason.&lt;/p&gt;
&lt;p&gt;If you constraint the scroll view to the top layout guide, the child stack view will be pushed down 64 pixels. That is 20 pixels for status bar and 44 pixels for navigation item. So you would need to change the constant to –64 pixels to stick the stack view to the top of the view. It is a fix but seems more of a hack than a real fix.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/xcode-layout-constraint/xcode-layout-constraint-50B57683-E48C-424C-A02D-F4CE074AE4F5.png&quot; alt=&quot;xcode-layout-constraint-50B57683-E48C-424C-A02D-F4CE074AE4F5.png&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Why is this happening?&lt;/h3&gt;
&lt;p&gt;This is caused by constraining the scroll view to the top layout guide instead of the superview. But there is no obvious way to constraint the top of the scroll view to the top of main UIView.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/xcode-layout-constraint/xcode-layout-constraint-E12954D5-65F7-454A-8F63-6B64CBAA62F9.png&quot; alt=&quot;xcode-layout-constraint-E12954D5-65F7-454A-8F63-6B64CBAA62F9.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;￼ The common effort of using ctrl+drag will only offer you to constraint the scroll view to the top layout guide. Using the tie-fighter icon will do the same as well.&lt;/p&gt;
&lt;p&gt;After you set up the constraint to the top layout guide and tried to edit it, there’s no way to change it to the superview either.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/xcode-layout-constraint/xcode-layout-constraint-64100186-FC9A-483E-9820-2EED8CD66ABF.png&quot; alt=&quot;xcode-layout-constraint-64100186-FC9A-483E-9820-2EED8CD66ABF.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;￼&lt;/p&gt;
&lt;h3&gt;So how the heck do we constraint the scroll view to the superview?&lt;/h3&gt;
&lt;p&gt;It is not obvious. After some searching, I found out that we can click the little arrow button (dropdown?) when setting constraint constant from the tie-fighter icon. From there you can select if you want to constraint the view to the superview or the top layout guide.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/xcode-layout-constraint/xcode-layout-constraint-CD2492A8-7130-4CFF-8D45-529F6D74B1FB.png&quot; alt=&quot;xcode-layout-constraint-CD2492A8-7130-4CFF-8D45-529F6D74B1FB.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Voila, problem solved.&lt;/p&gt;
&lt;h3&gt;What should have happened&lt;/h3&gt;
&lt;p&gt;Given the hidden nature of the feature, maybe Xcode could have done better to inform us about this option.&lt;/p&gt;
&lt;p&gt;When ctrl+dragging, a small tooltip should show up to a ask us if we want to constraint either to the top/bottom layout guide or the superview. This wouldn’t be a big problem because it should only be shown to a view that is directly under the main UIView. The other nested views should work normally.&lt;/p&gt;
&lt;p&gt;When editing an existing constraint, there should be an option to change from top layout guide to superview and vice versa. Currently, the only way is to delete the constraint and recreate them.&lt;/p&gt;
&lt;p&gt;Xcode could have done better.&lt;/p&gt;
</content:encoded></item><item><title>How to Read 62 Books in a Year</title><link>https://www.augusteo.com/blog/read-62-books/</link><guid isPermaLink="true">https://www.augusteo.com/blog/read-62-books/</guid><description>How to Read 62 Books in a Year - a blog post by Victor Augusteo</description><pubDate>Fri, 20 Jan 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;From 2012 to 2016 I’ve read &lt;strong&gt;310&lt;/strong&gt; books, which averages to &lt;strong&gt;62&lt;/strong&gt; books per year. All the while working full time jobs, learning new languages, traveling, three weddings (to the same bride, of course), watched heaps of movies and TV shows, etc.&lt;/p&gt;
&lt;p&gt;The point is that reading lots of books can be effortless. You don’t have to suddenly transform yourself into a reclusive bookworm. I’m going to share how I did it. It may or may not work, but give it a try and you might surprise yourself&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“A reader lives a thousand lives before he dies, said Jojen. The man who never reads lives only one.”― George R.R. Martin, A Dance with Dragons&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;Track your reading&lt;/h1&gt;
&lt;p&gt;The first step is to set a goal of how many books you wanted to read this year. You don’t have to go straight to 52 (one a week). Start small. 12 is plenty and it will give you a full month to read one book.&lt;/p&gt;
&lt;p&gt;I use two tools for this:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Goodreads reading challenge&lt;/strong&gt; You should register a Goodreads account and start your challenge here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/challenges/5493-2017-reading-challenge&quot;&gt;2017 Reading Challenge&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/challenges/5493-2017-reading-challenge&quot;&gt;1178957 people participating - Motivate yourself to read more books: Enter the 2017 Reading Challenge!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Writing down books that you wanted to read&lt;/strong&gt; I’m a fan of &lt;a href=&quot;http://bulletjournal.com/&quot;&gt;Bullet Journal&lt;/a&gt; and on New Year’s day, I wrote a list of books that I wanted to read for the year. Here’s the 2017 listing:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/read-62-books/read-62-books-A088BC33-5598-4F7F-86D9-624BE033C046.png&quot; alt=&quot;read-62-books-A088BC33-5598-4F7F-86D9-624BE033C046.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Here are some list of places where you can find inspiration:&lt;/p&gt;
&lt;p&gt;Ok now that you have setup the challenges and list, you should start reading more. ￼&lt;/p&gt;
&lt;h1&gt;Read whenever you can&lt;/h1&gt;
&lt;p&gt;Hours of our life are spent on commutes and chores that doesn’t require our full attention. You should read/listen to audiobooks during those:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Commute to and from work&lt;/li&gt;
&lt;li&gt;Gardening, lawn mowing&lt;/li&gt;
&lt;li&gt;Washing dishes, vacuuming and mopping the floor&lt;/li&gt;
&lt;li&gt;Walking your dog&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Many people would just browse Facebook or the news while commuting on the public transport. While that satisfies your boredom, it isn’t really beneficial for your mind. You should use that time to read books instead. For this purpose, getting a good ebook reader would help you greatly as they are lighter and easier to carry. I use the Amazon Kindle Voyage that’s more than 2 years old but still works well. If you prefer the printed paper, then by all means carry the paper book.&lt;/p&gt;
&lt;p&gt;If you are driving, then use the time to play audiobook on your car’s stereo!&lt;/p&gt;
&lt;p&gt;You would notice the rest of the listed activities, hands and eyes are undisposed. This brings me to the next point:&lt;/p&gt;
&lt;h3&gt;Audiobooks— Learn it, live it, love it&lt;/h3&gt;
&lt;p&gt;￼ The major advantage of audiobook is that you free up your eyes and hands to do other thing while listening. This technique will be the main contributor of your upcoming prolific reading streak as it enables you to read while doing other stuff.&lt;/p&gt;
&lt;p&gt;Invest in a pair of nice noice cancelling headphones if you can. I highly recommend the &lt;strong&gt;Bose QC35&lt;/strong&gt;. It is wireless, so you don’t have to worry about cables. It might seem like a minor thing, but you won’t believe the difference it will make. If the price seems high to you, there are numerous deals going on throughout the year. I use &lt;a href=&quot;https://www.ozbargain.com.au/product/bose-quietcomfort-35&quot;&gt;OzBargain&lt;/a&gt; to track discounts. Bought it at 20% off RRP.&lt;/p&gt;
&lt;p&gt;No matter what headphone that you get, get used to wearing it. Doing chores at home won’t be boring now!&lt;/p&gt;
&lt;h3&gt;Speed listening&lt;/h3&gt;
&lt;p&gt;Once you load up your phone with audiobooks, you should try listening at faster speed.&lt;/p&gt;
&lt;p&gt;Start at 1x, then 1.5x, then 2x. I usually have 2x as base speed and increase according to the books. Some narrator has a calmer voice that you can listen at 3x while still understanding the words. Some books like &lt;a href=&quot;https://www.goodreads.com/series/40911-saga-o-wied-minie&quot;&gt;the Witcher series&lt;/a&gt; are narrated with many voices, so I stayed on 2 to 2.5x for such books.&lt;/p&gt;
&lt;p&gt;Many players offer up to 3x playback speed. I personally use:&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;To recap, there aren’t any secret to this. Those tips will help you, but the work is still in your hands.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Track your reading with Goodreads or journals&lt;/li&gt;
&lt;li&gt;Read whenever your can, listen to audiobooks whenever u can’t&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So how many books will you read this year?&lt;/p&gt;
</content:encoded></item><item><title>Start Contributing to Open Source</title><link>https://www.augusteo.com/blog/contribute-open-source/</link><guid isPermaLink="true">https://www.augusteo.com/blog/contribute-open-source/</guid><description>Start Contributing to Open Source - a blog post by Victor Augusteo</description><pubDate>Sun, 27 Nov 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Just recently my first &lt;a href=&quot;https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3307&quot;&gt;PR to ReactiveCocoa&lt;/a&gt; has been merged and I’m still excited.&lt;/p&gt;
&lt;p&gt;I have been using ReactiveCocoa/ReactiveSwift since the start of 2016 and enjoying it very much. The learning curve is not easy, but once you grok it, its incredible.&lt;/p&gt;
&lt;p&gt;This brings us to contributing to open source. IMO the easiest way would be:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use open source codes&lt;/li&gt;
&lt;li&gt;Find functionality that you wanted but is lacking&lt;/li&gt;
&lt;li&gt;Fork the repo and make a new branch with the changes along with the tests&lt;/li&gt;
&lt;li&gt;Submit Pull Request with you new branch&lt;/li&gt;
&lt;li&gt;Action the change requests and code styling that many repo enforces&lt;/li&gt;
&lt;li&gt;Voila!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That’s what I did in the ReactiveCocoa PR. I wanted to bind &lt;code&gt;attributedText&lt;/code&gt; of a &lt;code&gt;UITextView&lt;/code&gt;from a &lt;code&gt;MutableProperty&amp;lt;NSAttributedString&amp;gt;&lt;/code&gt; but the framework doesn’t support that.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;textView.reactive.attributedText &amp;lt;~ attributedStringProperty
  .producer
  .map {
    // whatever
  }

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without the binding, I have to use &lt;code&gt;didSet { }&lt;/code&gt; on the property change, which isn’t that bad, but could be better.&lt;/p&gt;
&lt;p&gt;So my colleague suggested to submit PR with the changes that I wanted, and I did!&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Further resources&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;There are several guides for open source newbies:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.firsttimersonly.com/&quot;&gt;First Timers Only&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://help.github.com/articles/where-can-i-find-open-source-projects-to-work-on/&quot;&gt;Github Guide&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://guides.github.com/activities/contributing-to-open-source/&quot;&gt;Another Github Guide&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Good luck!&lt;/p&gt;
</content:encoded></item><item><title>Reactive Way to Handle Dismissing Modal View Controller</title><link>https://www.augusteo.com/blog/reactive-dismiss-vc/</link><guid isPermaLink="true">https://www.augusteo.com/blog/reactive-dismiss-vc/</guid><description>Reactive Way to Handle Dismissing Modal View Controller - a blog post by Victor Augusteo</description><pubDate>Fri, 18 Nov 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We use modal presentation quite often in iOS. You must be familiar with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;self.present(vc, animated: true, completion: nil)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now lets say we want something to happen with our current VC when the modal VC is dismissed, there are several ways to do that.&lt;/p&gt;
&lt;p&gt;The Cocoa way would be to use &lt;em&gt;presenting view controller&lt;/em&gt;, &lt;em&gt;delegate pattern&lt;/em&gt; or &lt;em&gt;notification center&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Lets go through each briefly.&lt;/p&gt;
&lt;h2&gt;Presenting view controller&lt;/h2&gt;
&lt;p&gt;This option is the worst because the child/presented VC needs to know quite a lot of detail about the parent/presenting VC. That should almost never be the case. The child should know as little about the parent as possible. Therefore lets avoid this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class ModalVC: UIViewController {
  func someFunc() {
    dismiss(animated: true, completion: {
      if let parent = self.presentingViewController {
        parent.doSomething()
      }
    })
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next pattern is a direct improvement over &lt;code&gt;presentingViewController&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Delegate pattern&lt;/h2&gt;
&lt;p&gt;Create a protocol with a void method and get the parent VC to conform to it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protocol ModalHandler {
  func modalDismissed()
}

class ParentVC: UIViewController, ModalHandler {
  func modalDismissed() {
    // do something
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then set the ParentVC as delegate of ModalVC before presentation and call the method during dismissal.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class ModalVC: UIViewController {
  func someFunc() {
    dismiss(animated: true, completion: {
      delegate.modalDismissed()
    })
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way is better that using &lt;code&gt;presentingViewController&lt;/code&gt; because the child doesn’t know about the parent, but only the protocol/interface as a layer of abstraction. I recommend using this pattern in a non reactive projects.&lt;/p&gt;
&lt;h2&gt;Notification pattern&lt;/h2&gt;
&lt;p&gt;With this, we need to get ParentVC to observe the modal dismissed notification.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class ParentVC: UIViewController {
  override func viewDidLoad() {
    NotificationCenter.default.addObserver(self, selector: #selector(ParentVC.handleModalDismissed), name: NSNotification.Name(rawValue: &quot;modalIsDimissed&quot;), object: nil)
  }

  func handleModalDismissed() {
    // Do something
  }
}

class ModalVC: UIViewController {
  func someFunc() {
    dismiss(animated: true, completion: {
      NotificationCenter.default.post(name: NSNotification.Name(rawValue: &quot;modalIsDimissed&quot;), object: nil)
    })
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I don’t like notifications because both parent and modal VC needs to know the notification name to work.&lt;/p&gt;
&lt;h2&gt;Reactive pattern&lt;/h2&gt;
&lt;p&gt;When you use ReactiveCocoa/ReactiveSwift, you could easily handle modal dismissal with several lines of codes in the presenting VC. This is my preferred way because the child doesn’t even need to do any extra work. All the heavy lifting is done by &lt;code&gt;ReactiveCocoa&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class ParentVC: UIViewController {
  override func viewDidLoad() {
    let modalVC = ModalVC()

    modalVC.reactive
      .trigger(for: #selector(onboardNav.viewDidDisappear(_:)))
      .observe { _ in self.handleModalDismissed() }

    present(modalVC, animated: false, completion: nil)
  }

  func handleModalDismissed() { }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In here, the ParentVC will observe modalVC before presenting. When modalVC calls &lt;code&gt;viewDidDisappear(_:)&lt;/code&gt;, ParentVC will call &lt;code&gt;self.handleModalDismissed()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As you can see, it is very clean and quite easy to understand even if you aren’t as familiar with reactive concepts.&lt;/p&gt;
</content:encoded></item><item><title>Easy Functional and Integration Testing with Mock Server | Tutorial and Example</title><link>https://www.augusteo.com/blog/functional-testing/</link><guid isPermaLink="true">https://www.augusteo.com/blog/functional-testing/</guid><description>Easy Functional and Integration Testing with Mock Server | Tutorial and Example - a blog post by Victor Augusteo</description><pubDate>Thu, 27 Oct 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You can download the &lt;a href=&quot;https://hugo.augusteo.com/blogFiles/MockServerPresenation.pdf&quot;&gt;presentation slides here.&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Basic Testing&lt;/h1&gt;
&lt;p&gt;Increasingly, more and more mobile apps rely on some kind of api/backend server to work. Be it to save user data, fetch new data, or syncing live chat. Testing api integration usually require &lt;a href=&quot;http://martinfowler.com/articles/mocksArentStubs.html&quot;&gt;mocking/stubbing&lt;/a&gt; the network calls.&lt;/p&gt;
&lt;p&gt;There are three basic layers of testing:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Unit test&lt;/strong&gt; - making sure each function/unit of code works as expected&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Functional test&lt;/strong&gt; - making sure units interact with each other as expected&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Integration test&lt;/strong&gt; - making sure our app integrate with other app/api/services as expected&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Mock server helps with all layers of tests, although it is particularly useful for functional and integration.&lt;/p&gt;
&lt;h1&gt;Why Use Mock Server&lt;/h1&gt;
&lt;h3&gt;1. Reuse&lt;/h3&gt;
&lt;p&gt;Practically everyone deploys on multiple platform nowadays. Java on Android, JS on web, Swift on iOS and unless you do full hybrid like Xamarin or React Native, you will need to mock the web services for testing in each and every separate languages.&lt;/p&gt;
&lt;p&gt;Mock server promotes sharing and code reuse, write once and use to test ios/android/web app. No need to write mock several times for different platform. You can just connect each app to the mock server and done.&lt;/p&gt;
&lt;h3&gt;2. Reducing Dependency of API&lt;/h3&gt;
&lt;p&gt;In the times when the API team is delayed/not in sync with the front end team’s sprint, mock server allows us to write our app and test agaisnt the mock server. This also applies when API server is down for whatever reason.&lt;/p&gt;
&lt;p&gt;This reduces dependency on each other and reduces coupling, which is always a good thing.&lt;/p&gt;
&lt;h3&gt;3. Debugging&lt;/h3&gt;
&lt;p&gt;Sometimes it is hard to reproduce a bug, especially when the bug is triggered by a specific response from the server. You could either modify your code temporarily to mimic that incoming response, or you could just make the mock server return a specific bug-causing response.&lt;/p&gt;
&lt;h3&gt;4. Predictability and Consistency&lt;/h3&gt;
&lt;p&gt;When you use remote server to test, and it comes back with error or high latency, it is very hard to determine what’s wrong. How do you know the fault lies with the network connection or with the server?&lt;/p&gt;
&lt;p&gt;Since mock server lives on your local machine, it is very easy see if it isn’t working properly.&lt;/p&gt;
&lt;h1&gt;What is Mock Server&lt;/h1&gt;
&lt;p&gt;Mock server is just a web server that will give us consistent response for particular request, that’s it. The only requirement is that the request and response format need to be consistent with the production server so that our test wouldn’t be able to tell the difference.&lt;/p&gt;
&lt;p&gt;The main benefit is that the mock server is operated and maintained for the benefit of the development team, so you can make it as simple or as complex as you need it to.&lt;/p&gt;
&lt;p&gt;It is just another tool in our toolbox. A very helpful one.&lt;/p&gt;
&lt;h1&gt;How to Use Mock Server&lt;/h1&gt;
&lt;p&gt;There are several existing solutions that’s quite popular. If you Google ‘mock server’, you will find:&lt;/p&gt;
&lt;p&gt;If you love Objective-C, there is an embedded web server called &lt;a href=&quot;https://github.com/swisspol/GCDWebServer&quot;&gt;GCDWebServer&lt;/a&gt; that will run on your Mac.&lt;/p&gt;
&lt;p&gt;You should use whichever stack that your team is familiar with or the stack that the backend team uses. When you have some problem, you could always give them a shout!&lt;/p&gt;
&lt;p&gt;For our tutorial, we will just write one from scratch using a combination of:&lt;/p&gt;
&lt;h1&gt;Basic Node &amp;amp; Express&lt;/h1&gt;
&lt;p&gt;Make sure that you have installed Node from the link above. For this tutorial, also please install &lt;a href=&quot;https://yarnpkg.com/en/docs/install&quot;&gt;Yarn&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Open up your terminal and start by making an empty directory and going into it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ mkdir mock
$ cd mock

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can initialize an empty app. When it asks question, you can either enter your app detail, or just keep pressing &lt;code&gt;enter&lt;/code&gt; key to use the default value.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$  yarn init
yarn init v0.16.1
question name (mock2):
question version (1.0.0):
question description:
question entry point (index.js):
question git repository:
question author:
question license (MIT):
success Saved package.json
✨  Done in 59.45s.

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when you list the files with &lt;code&gt;ls&lt;/code&gt;, you should see only one &lt;code&gt;package.json&lt;/code&gt; file in the directory. Let continue by installing Express and Morgan.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ yarn add express morgan

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After a while, it should download and install both packages. You can check by listing the files. Now we can start writing our basic Express app.&lt;/p&gt;
&lt;p&gt;Let’s create a new file called &lt;code&gt;app.js&lt;/code&gt; and open with with your favourite text editor. I will give example using Vim:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ vim app.js

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside the file, copy paste this block:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 1
const express = require(&apos;express&apos;),
app = express(),
morgan = require(&apos;morgan&apos;);

// 2
app.use(morgan(&apos;dev&apos;));

// 3
app.get(&apos;/&apos;, function (req, res) {
  res.send(&apos;Hello World!&apos;);
});

// 4
app.listen(5678, function () {
   console.log(&apos;listening on port 5678!&apos;);
});

&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Here we tell Node that we require Express and Morgan, and that we assign &lt;code&gt;app&lt;/code&gt; constant to the express module&lt;/li&gt;
&lt;li&gt;We tell Express to use Morgan with the profile &lt;code&gt;&apos;dev&apos;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;We tell Express to send &lt;code&gt;&apos;Hello World!&apos;&lt;/code&gt; when a GET request is sent to &lt;code&gt;&apos;/&apos;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Lastly we tell Express to listen to port &lt;code&gt;5678&lt;/code&gt; and prints a console log with that message&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To test this, just run &lt;code&gt;node app.js&lt;/code&gt; in the folder, and you should get &lt;code&gt;listening on port 5678!&lt;/code&gt; message printed. To test this, you can open &lt;a href=&quot;http://localhost:5678/&quot;&gt;http://localhost:5678/&lt;/a&gt; in your web browser to see ‘Hello World!’ printed.&lt;/p&gt;
&lt;p&gt;Congrats, your have just built your first Node + Express app!&lt;/p&gt;
&lt;p&gt;Now if you check out the terminal window where you run the app, you should see:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET / 200 2.518 ms - 12

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That message is from Morgan and it serves just a logging purposes to tell us when our server get hit by requests.&lt;/p&gt;
&lt;p&gt;We will revisit the mock server later, now we will have a look at the sample app that we are going to test.&lt;/p&gt;
&lt;h1&gt;Favourite Food app&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/functional-testing/functional-testing-Screenshot2016-11-1211.42.10.jpg&quot; alt=&quot;functional-testing-Screenshot2016-11-1211.42.10.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You can clone &lt;a href=&quot;https://github.com/augusteo/FavouriteFood&quot;&gt;this repo&lt;/a&gt; to get a quick start.&lt;/p&gt;
&lt;p&gt;This is a very simple app that will return the name of your favourite food when you put your name on. Right now the app only supports ‘foo’ and ‘bar’.&lt;/p&gt;
&lt;p&gt;There are only two noteworthy file in the project: &lt;code&gt;ViewController.swift&lt;/code&gt; and &lt;code&gt;Favourite_FoodUITests.swift&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ViewController&lt;/code&gt; manages the network request and UI interaction while the test file, well, manages the UI Test.&lt;/p&gt;
&lt;p&gt;There are two schemes; One to hit production remote API, and the other to hit mock server.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/functional-testing/functional-testing-Screenshot2016-11-1211.59.25.jpg&quot; alt=&quot;functional-testing-Screenshot2016-11-1211.59.25.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Lets run the app using the &lt;code&gt;Favourite Food&lt;/code&gt; scheme for now on the simulator. When it run, you can type &lt;code&gt;foo&lt;/code&gt; as name and press &lt;code&gt;calculate&lt;/code&gt;, it should return &lt;code&gt;Pizza&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It should return &lt;code&gt;Roast Pork Knucle&lt;/code&gt; when you enter &lt;code&gt;bar&lt;/code&gt; as name.&lt;/p&gt;
&lt;p&gt;Now if you rerun the app with &lt;code&gt;FF-Mock&lt;/code&gt; scheme, it will crash when you press &lt;code&gt;calculate&lt;/code&gt; because our mock server doesn’t know how to handle the request. Let’s fix that.&lt;/p&gt;
&lt;p&gt;We will cover the multiple scheme for multiple api in another post.&lt;/p&gt;
&lt;h1&gt;Mock server for Favourite Food&lt;/h1&gt;
&lt;p&gt;lets open the &lt;code&gt;app.js&lt;/code&gt; of our mock server again.&lt;/p&gt;
&lt;p&gt;add this code block just above the &lt;code&gt;app.listen&lt;/code&gt; line.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app.get(&apos;/3l2z2&apos;, function (req, res) {
  let favJson = {
    &quot;foo&quot;: &quot;Pizza&quot;,
    &quot;bar&quot;: &quot;Roast Pork Knuckle&quot;
  };
  res.send(favJson);
});

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So your &lt;code&gt;app.js&lt;/code&gt; file should look like this now:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const express = require(&apos;express&apos;),
app = express(),
morgan = require(&apos;morgan&apos;);

app.use(morgan(&apos;dev&apos;));

app.get(&apos;/&apos;, function (req, res) {
  res.send(&apos;Hello World!&apos;);
});

app.get(&apos;/3l2z2&apos;, function (req, res) {
  let favJson = {
    &quot;foo&quot;: &quot;Pizza&quot;,
    &quot;bar&quot;: &quot;Roast Pork Knuckle&quot;
  };
  res.send(favJson);
});

app.listen(5678, function () {
  console.log(&apos;listening on port 5678!&apos;);
});

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All this block of code does, it to tell Express to return &lt;code&gt;favJson&lt;/code&gt; object when &lt;code&gt;/3l2z2&lt;/code&gt; is hit by request. Simple.&lt;/p&gt;
&lt;p&gt;Now lets kill the previous node instance and rerun it again. to do this, just go to the terminal window where you run &lt;code&gt;node app.js&lt;/code&gt; before and press &lt;code&gt;ctrl+c&lt;/code&gt; to cancel it. Then rerun &lt;code&gt;node app.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To confirm that the new json is up, open &lt;a href=&quot;http://localhost:5678/3l2z2&quot;&gt;http://localhost:5678/3l2z2&lt;/a&gt; on your web browser to check. you should see:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&quot;foo&quot;:&quot;Pizza&quot;,&quot;bar&quot;:&quot;Roast Pork Knuckle&quot;}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Awesome. Now if you run the iOS project with &lt;code&gt;FF-Mock&lt;/code&gt; scheme, you should be getting Pizza and Roast Pork Knuckle successfully.&lt;/p&gt;
&lt;h1&gt;Intro to UI Test&lt;/h1&gt;
&lt;p&gt;Lets get back to Xcode and set the scheme to &lt;code&gt;FavouriteFood&lt;/code&gt;. Then press &lt;code&gt;cmd+u&lt;/code&gt; to run the tests. You will see that the simulator would launch and the app would run by itself.&lt;/p&gt;
&lt;p&gt;To see what’s happening, lets open the &lt;code&gt;Favourite_FoodUITests.swift&lt;/code&gt; file and take a look at one of the function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func testFoo() {
  let textField = app.textFields[&quot;tf&quot;]  // 1
  textField.tap()                       // 2
  textField.typeText(&quot;foo&quot;)             // 3
  app.buttons[&quot;Calculate&quot;].tap()        // 4
  expectation(for: exists, evaluatedWith: app.staticTexts[&quot;Pizza&quot;], handler: nil)
  waitForExpectations(timeout: 3, handler: nil) //5
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Explanation:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Find the text field with accesibility label &lt;code&gt;tf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Taps the field to bring up keyboard&lt;/li&gt;
&lt;li&gt;Types &lt;code&gt;foo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Taps the calculate button&lt;/li&gt;
&lt;li&gt;Waits for 3 second for the app to show &lt;code&gt;Pizza&lt;/code&gt; text&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As you can see it is quite simple to write and run UI Test.&lt;/p&gt;
&lt;p&gt;Now since our mock server is up and running, we can change the scheme to &lt;code&gt;FF-Mock&lt;/code&gt; and press &lt;code&gt;cmd+u&lt;/code&gt; to test it against the mock server. You should see that it is running correctly.&lt;/p&gt;
&lt;h1&gt;Simple Express Refactor&lt;/h1&gt;
&lt;p&gt;This express app works well because we only have one endpoint request. Imagine if we have 300 endpoints to take care of, we couldn’t just fit everything into &lt;code&gt;app.js&lt;/code&gt;. To make things cleaner, we will refactor the app to different files.&lt;/p&gt;
&lt;p&gt;First lets create a new directory instead of your &lt;code&gt;mock&lt;/code&gt; directory, then make a file named &lt;code&gt;fav.js&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ mkdir controllers
$ cd controllers
$ touch fav.js

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then open &lt;code&gt;fav.js&lt;/code&gt; with your favourite editor and paste this block:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;exports.index = (req, res) =&amp;gt; {
  let favJson = {
    &quot;foo&quot;: &quot;Pizza&quot;,
    &quot;bar&quot;: &quot;Roast Pork Knuckle&quot;
  };
  res.send(favJson);
};

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All this does is exporting a function called &lt;code&gt;index&lt;/code&gt; that returns &lt;code&gt;favJson&lt;/code&gt; as response. Very similar to the one in &lt;code&gt;app.js&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Save this file. Then open up &lt;code&gt;app.js&lt;/code&gt; and replace this block:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app.get(&apos;/3l2z2&apos;, function (req, res) {
  let favJson = {
    &quot;foo&quot;: &quot;Pizza&quot;,
    &quot;bar&quot;: &quot;Roast Pork Knuckle&quot;
  };
  res.send(favJson);
});

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const favController = require(&apos;./controllers/fav&apos;);

app.get(&apos;/3l2z2&apos;, favController.index);

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This replacement simply tells Express to ask &lt;code&gt;fav.js&lt;/code&gt; to perform &lt;code&gt;index&lt;/code&gt; function when a GET request is sent to &lt;code&gt;/3l2z2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you have more parts in the mock server, such as user management or adding new food, feel free to create new controllers and use the same &lt;code&gt;require&lt;/code&gt; syntax.&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;We have learned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why, What, How of mock server&lt;/li&gt;
&lt;li&gt;UITest in iOS&lt;/li&gt;
&lt;li&gt;Node + Express app to act as mock server&lt;/li&gt;
&lt;li&gt;Refactor Express&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What was covered in the talk but didn’t make it to this post:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using schemes in Xcode to manage multiple URLs&lt;/li&gt;
&lt;li&gt;Using &lt;a href=&quot;https://fastlane.tools/&quot;&gt;Fastlane&lt;/a&gt; to run tests against mock server on CI server&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those topics are better served by their own dedicated post.&lt;/p&gt;
</content:encoded></item><item><title>Simplest Way to Sync Dotfiles and Config Using Git</title><link>https://www.augusteo.com/blog/sync-dotfiles/</link><guid isPermaLink="true">https://www.augusteo.com/blog/sync-dotfiles/</guid><description>Simplest Way to Sync Dotfiles and Config Using Git - a blog post by Victor Augusteo</description><pubDate>Sun, 23 Oct 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Most devs have two or more computers that they use. In my case it is my home Macbook and one in the office. We want to have the same config, keybinding or aliases across those machines.&lt;/p&gt;
&lt;p&gt;There are several ways to do it. I tried using symlinks to a folder that is synced through git, but some apps won’t load symlinked files. For example, if you try to symlink &lt;code&gt;~/.zshrc&lt;/code&gt; then zsh will load as if you don’t have an rc file.&lt;/p&gt;
&lt;p&gt;After looking around for a while, I came upon a post in Hackernews about this issue. It led to this &lt;a href=&quot;https://developer.atlassian.com/blog/2016/02/best-way-to-store-dotfiles-git-bare-repo/&quot;&gt;article by Atlassian&lt;/a&gt;. It is by far the best solution.&lt;/p&gt;
&lt;h1&gt;Git Bare&lt;/h1&gt;
&lt;p&gt;This technique is using &lt;code&gt;git init --bare&lt;/code&gt; which I have never encountered before. You don’t really need to understand it to use it, but for reference sake, this is what &lt;code&gt;--help&lt;/code&gt; says.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;--bare
Create a bare repository. If GIT_DIR environment is not set, it is set to the current working directory.

&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Setup&lt;/h1&gt;
&lt;p&gt;That article is very succint and you could just follow the steps laid out there. In summary, use these commands to set it up.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git init --bare $HOME/.cfg
alias config=&apos;/usr/bin/git --git-dir=$HOME/.cfg/ --work-tree=$HOME&apos;
config config --local status.showUntrackedFiles no
echo &quot;alias config=&apos;/usr/bin/git --git-dir=$HOME/.cfg/ --work-tree=$HOME&apos;&quot; &amp;gt;&amp;gt; $HOME/.bashrc

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace the last &lt;code&gt;.bashrc&lt;/code&gt; to whatever shell that you are using e.g. in my case it is &lt;code&gt;.zshrc&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;Operation&lt;/h1&gt;
&lt;p&gt;All git operation are permitted, you just have to use &lt;code&gt;config&lt;/code&gt; instead of &lt;code&gt;git&lt;/code&gt;. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;config status
config add ~/.zshrc
config commit -m &quot;Add vimrc&quot;
config add ~/.xvimrc
config commit -m &quot;Add xvimrc&quot;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To sync those files with another computer, you need to setup a repo. I recommend using &lt;a href=&quot;https://gitlab.com/&quot;&gt;Gitlab&lt;/a&gt; as they have free private repos. After creating new repo in Gitlab, you can do:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;config remote add origin git@gitlab.com:yourname/testrepo.git
config push -u origin master

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From this point on, you can treat it like normal git repo. One interesting use case would be to create different branches for different settings that you wanted to use.&lt;/p&gt;
&lt;h1&gt;Setting up new machines&lt;/h1&gt;
&lt;p&gt;Now that you have the first machine set up and syncing nicely, you can start setting up the second machine by first adding this line to .zshrc or .bashrc.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;alias config=&apos;/usr/bin/git --git-dir=$HOME/.cfg/ --work-tree=$HOME&apos;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then clone the repo from Gitlab to the folder:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone --bare git@gitlab.com:yourname/testrepo.git $HOME/.cfg

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From here, you can &lt;code&gt;pull&lt;/code&gt;, &lt;code&gt;push&lt;/code&gt;, &lt;code&gt;merge&lt;/code&gt; and &lt;code&gt;checkout&lt;/code&gt; to your hearts content.&lt;/p&gt;
&lt;p&gt;You really should read the &lt;a href=&quot;https://developer.atlassian.com/blog/2016/02/best-way-to-store-dotfiles-git-bare-repo/&quot;&gt;source article&lt;/a&gt; since it explains the steps in detail except adding remote repo.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</content:encoded></item><item><title>iOS10 Crash - New Privacy Permission Settings and Descriptions</title><link>https://www.augusteo.com/blog/crash-privacy/</link><guid isPermaLink="true">https://www.augusteo.com/blog/crash-privacy/</guid><description>iOS10 Crash - New Privacy Permission Settings and Descriptions - a blog post by Victor Augusteo</description><pubDate>Tue, 04 Oct 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;So we were getting crashes on development build when our app tries to ask user for camera permission on iOS10. It hasn’t happened on iOS9, and we didn’t know what went wrong. Setting up exception breakpoint on throw and catch doesn’t help either. Apple should really give us more clue on this. I was stuck for a while.&lt;/p&gt;
&lt;p&gt;Until my team mate said that it might be the new privacy settings.&lt;/p&gt;
&lt;p&gt;Lo and behold, he is right, turns out there are heaps of new privacy permission request description that we have to add now. Prior to iOS10, only location permission was required. The full list are:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;NSBluetoothPeripheralUsageDescription
NSCalendarsUsageDescription
NSVoIPUsageDescription
NSCameraUsageDescription
NSContactsUsageDescription
NSHealthShareUsageDescription
NSHealthUpdateUsageDescription
NSHomeKitUsageDescription
NSLocationUsageDescription
NSLocationAlwaysUsageDescription
NSLocationWhenInUseUsageDescription
NSAppleMusicUsageDescription
NSMicrophoneUsageDescription
NSMotionUsageDescription
NSPhotoLibraryUsageDescription
NSRemindersUsageDescription
NSSpeechRecognitionUsageDescription
NSSiriUsageDescription
NSVideoSubscriberAccountUsageDescription

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the case of our app, adding &lt;code&gt;NSCameraUsageDescription&lt;/code&gt; into &lt;code&gt;info.plist&lt;/code&gt; fix the crashes and display a description message under the &lt;code&gt;App would like to access your camera&lt;/code&gt; system alert.&lt;/p&gt;
</content:encoded></item><item><title>Binary Search Algorith with Recursion and Generics - Swift Tutorial</title><link>https://www.augusteo.com/blog/swift-binary-search/</link><guid isPermaLink="true">https://www.augusteo.com/blog/swift-binary-search/</guid><description>Binary Search Algorith with Recursion and Generics - Swift Tutorial - a blog post by Victor Augusteo</description><pubDate>Fri, 26 Aug 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Following through with my &lt;a href=&quot;https://hugo.augusteo.com/swift-quick-sort-algorithm-with-recursion-and-generic&quot;&gt;last post&lt;/a&gt;, we will explore how to implement the &lt;a href=&quot;https://www.khanacademy.org/computing/computer-science/algorithms/binary-search/a/implementing-binary-search-of-an-array&quot;&gt;binary search&lt;/a&gt; algorithm in Swift. Of course it has to be generic too!&lt;/p&gt;
&lt;h1&gt;Problem and assumption&lt;/h1&gt;
&lt;p&gt;This algorithm assumes that we have an array of comparable objects. That is to say, the compiler should know how to compare those objects. If your object is not a primitive type, then you can follow the &lt;a href=&quot;https://hugo.augusteo.com/swift-quick-sort-algorithm-with-recursion-and-generic&quot;&gt;last post&lt;/a&gt; on how to make the struct/class/enum conform to &lt;code&gt;Comparable&lt;/code&gt; protocol.&lt;/p&gt;
&lt;p&gt;lets say we have an array of numbers:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let arr = [4,1,78,34,666,23,0,999,3245,54]

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can’t just apply binary search to this array because binary search assumes that the array is already sorted. So let’s do it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let arr = [4,1,78,34,666,23,0,999,3245,54].sort()
// [0, 1, 4, 23, 34, 54, 78, 666, 999, 3245]

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Please remember to sort your array before applying the search, because it wouldn’t return the correct result otherwise.&lt;/p&gt;
&lt;h1&gt;Algorithm&lt;/h1&gt;
&lt;p&gt;Binary search is very simple. We pass and array and a search target, then it will return the index of the target inside the array to us. We will return an optional nil in the case of target not found.&lt;/p&gt;
&lt;p&gt;The process in short, is as follow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Check if midpoint of the array is our target, if yes, just return the index&lt;/li&gt;
&lt;li&gt;If not, check if the target is greater than the midpoint&lt;/li&gt;
&lt;li&gt;If yes, search from midpoint to end of array recursively&lt;/li&gt;
&lt;li&gt;If not, search from start of array to midpoint recursively&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That’s it.&lt;/p&gt;
&lt;h1&gt;Swift code&lt;/h1&gt;
&lt;p&gt;I have used generics in this code because this assumes you have read the last post about it :)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func binarySearch&amp;lt;T: Comparable&amp;gt;(array: [T], target: T, range: Range&amp;lt;Int&amp;gt; = 0..&amp;lt;arr.count) -&amp;gt; Int? { // interface
  guard range.startIndex &amp;lt; range.endIndex else { return nil } // 1

  let midIndex = range.startIndex + (range.endIndex - range.startIndex) / 2 // 2
  guard array[midIndex] != target else { return midIndex } // 3

  let newRange = array[midIndex] &amp;gt; target ? range.startIndex..&amp;lt;midIndex
    : midIndex.advancedBy(1)..&amp;lt;range.endIndex // 4

  return binarySearch(array, target: target, range: newRange) // 5
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Interface&lt;/h3&gt;
&lt;p&gt;The function will take a generic type &lt;code&gt;T&lt;/code&gt; which conforms to &lt;code&gt;Comparable&lt;/code&gt; protocol. The array and target is of type T.&lt;/p&gt;
&lt;p&gt;We have a &lt;code&gt;Range&lt;/code&gt; of type &lt;code&gt;Int&lt;/code&gt; and not &lt;code&gt;T&lt;/code&gt; because a range just need to act as our index, which usually is &lt;code&gt;Int&lt;/code&gt;. We have also added a default value for this param so user of this func doesn’t need to specify the first range. The range is mostly for the recursion to work.&lt;/p&gt;
&lt;p&gt;It returns an &lt;code&gt;Optional Int&lt;/code&gt;. &lt;code&gt;Nil&lt;/code&gt; for not found, and &lt;code&gt;Int&lt;/code&gt; for index of target if found.&lt;/p&gt;
&lt;h3&gt;1. Range checking&lt;/h3&gt;
&lt;p&gt;This is the first recursion exit point. If the start of the range is not smaller than the end range, that means we couldn’t find the target. So just return nil&lt;/p&gt;
&lt;h3&gt;2. Index on the middle of the array&lt;/h3&gt;
&lt;p&gt;We specify the &lt;code&gt;midIndex&lt;/code&gt; of the array. We need to add the &lt;code&gt;startIndex +&lt;/code&gt; at the start to accomodate the recursion that starts if the target number is larger than midpoint.&lt;/p&gt;
&lt;h3&gt;3. Found the target!&lt;/h3&gt;
&lt;p&gt;The function will only proceed when the midpoint isn’t the target. If it is, the function will exit and returns the target index.&lt;/p&gt;
&lt;h3&gt;4. New range for recursion&lt;/h3&gt;
&lt;p&gt;When the midpoint is not the target, we have to find which half of the array to search. If the target is greater than midpoint, we will search the right side of the array and vice versa.&lt;/p&gt;
&lt;h3&gt;5. Recursion trigger&lt;/h3&gt;
&lt;p&gt;This is where we invoke recursion to re-search the midpoint of the same array, but with the new range that we calculated on &lt;code&gt;// 4&lt;/code&gt;. The recursion will keep going until either &lt;code&gt;// 1&lt;/code&gt; hits, meaning the target isn’t in the array or when &lt;code&gt;// 3&lt;/code&gt; hits meaning we found the target and just returns the index.&lt;/p&gt;
&lt;h1&gt;Testing&lt;/h1&gt;
&lt;p&gt;So now that we have a working search function, we can give it a try:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;binarySearch(arr, target: 23)  // 3
binarySearch(arr, target: 999) // 8
binarySearch(arr, target: 213) // nil
binarySearch(arr, target: 54)  // 5

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It worked!&lt;/p&gt;
&lt;p&gt;But how do we really check if it work? We can either manually go through the array and count the index of the target, or we can employ the Swift assertion, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;assert(binarySearch(arr, target: 23)  == arr.indexOf(23))
assert(binarySearch(arr, target: 213) == arr.indexOf(213))
assert(binarySearch(arr, target: 4)   == arr.indexOf(4))

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we employed the compiler assertion to check if our searching algorithm returns the same index as the standard library searching function. If the compiler doesn’t complain, it means our work is done!&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Binary search is a classic divide and conquer method for a more efficient searching. There are many ways to do it, but recursion is one of the funnest way!&lt;/p&gt;
</content:encoded></item><item><title>Swift Quick Sort Algorithm With Recursion and Generics</title><link>https://www.augusteo.com/blog/swift-quick-sort/</link><guid isPermaLink="true">https://www.augusteo.com/blog/swift-quick-sort/</guid><description>Swift Quick Sort Algorithm With Recursion and Generics - a blog post by Victor Augusteo</description><pubDate>Mon, 22 Aug 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There are many sorting algorithm and many way to implement it. &lt;a href=&quot;https://en.wikipedia.org/wiki/Quicksort&quot;&gt;Quicksort&lt;/a&gt; is one of the quickest.&lt;/p&gt;
&lt;p&gt;Today we will learn how to implement quicksort in Swift with generic and protocol conformance.&lt;/p&gt;
&lt;h1&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I was reading &lt;a href=&quot;http://learnyouahaskell.com/&quot;&gt;Learn You a Haskell&lt;/a&gt; book today and one of the example of recursion chapter was quicksort. It just tickled me to implement the same thing in Swift.&lt;/p&gt;
&lt;p&gt;Great book by the way, you should consider this &lt;a href=&quot;https://www.humblebundle.com/books/joy-of-coding-book-bundle&quot;&gt;Joy of coding&lt;/a&gt; bundle for more books.&lt;/p&gt;
&lt;h1&gt;Sorting numbers&lt;/h1&gt;
&lt;p&gt;Lets say we have this array of jumbled Int that we wanted to sort&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let arrNum = [6, 4, 7, 9, 22, 78, 11, 1, 0, 234]

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now lets define our function interface&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func quickSort(array: [Int]) -&amp;gt; [Int] {
  return array
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That codeblock will just return the unsorted array, which isn’t what we wanted. So lets get it working.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func quickSort(array: [Int]) -&amp;gt; [Int] {
  if array.isEmpty { return [] } // 1

  let first = array.first! // 2

  let smallerOrEqual = array.dropFirst().filter { $0 &amp;lt;= first } // 3
  let larger         = array.dropFirst().filter { $0 &amp;gt; first } // 4

  return quickSort(smallerOrEqual) + [first] + quickSort(larger) // 5
}

&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Every recursive function needs an escape scenario, else it would go to an infinite loop. In this case, we want the recursion to break when the array that is passed is empty.&lt;/li&gt;
&lt;li&gt;We need to store the first element of the array to compare it with the smaller or larger number.&lt;/li&gt;
&lt;li&gt;The first half of quicksort is all the values that are smaller or equal to our first number. We can filter the array after we drop the first value.&lt;/li&gt;
&lt;li&gt;The second half is all the values that are larger than our first number.&lt;/li&gt;
&lt;li&gt;The first and second half would then be recursed and added to the first value.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This graphic from &lt;a href=&quot;http://learnyouahaskell.com/&quot;&gt;Learn You a Haskell&lt;/a&gt; explains the concept perfectly:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/swift-quick-sort/swift-quick-sort-Screenshot2016-08-2222.07.41.jpg&quot; alt=&quot;swift-quick-sort-Screenshot2016-08-2222.07.41.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Let’s test it!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;quickSort(arrNum) // [0, 1, 4, 6, 7, 9, 11, 22, 78, 234]

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yay! It’s working!&lt;/p&gt;
&lt;h1&gt;Generics&lt;/h1&gt;
&lt;p&gt;As it is now, the function only works for &lt;code&gt;Int&lt;/code&gt; type. What do we do if we want to sort a &lt;code&gt;Double&lt;/code&gt; array?&lt;/p&gt;
&lt;p&gt;We can either copy paste the whole function and just change the type to &lt;code&gt;Double&lt;/code&gt; like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func quickSort(array: [Double]) -&amp;gt; [Double] {
  // implementation
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But then we will have to copy paste it again if we need to compare &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Float&lt;/code&gt; or other types.&lt;/p&gt;
&lt;p&gt;There must be a better way. And yes there is! We can use &lt;a href=&quot;https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Generics.html&quot;&gt;Generics&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func quickSort&amp;lt;T: Comparable&amp;gt;(array: [T]) -&amp;gt; [T] {
  if array.isEmpty { return [] }

  let first = array.first!

  let smallerOrEqual = array.dropFirst().filter { $0 &amp;lt;= first }
  let larger         = array.dropFirst().filter { $0 &amp;gt; first }

  return quickSort(smallerOrEqual) + [first] + quickSort(larger)
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the implementation of the method doesn’t change. Only the interface and type is changed.&lt;/p&gt;
&lt;p&gt;Our function will now be able to sort any types that conforms to the &lt;code&gt;Comparable&lt;/code&gt; protocol. Swift already implemented this for most of the primitive types that we encounter in daily basis.&lt;/p&gt;
&lt;p&gt;Let’s test it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let arrStr = [&quot;a&quot;, &quot;x&quot;, &quot;R&quot;, &quot;hey&quot;, &quot;c&quot;, &quot;Jon Snow&quot;]
let arrDouble = [1.4, 6.7, 9.2, 6.4, 1.4]

quickSort(arrNum)    // [0, 1, 4, 6, 7, 9, 11, 22, 78, 234]
quickSort(arrStr)    // [&quot;Jon Snow&quot;, &quot;R&quot;, &quot;a&quot;, &quot;c&quot;, &quot;hey&quot;, &quot;x&quot;]
quickSort(arrDouble) // [1.4, 1.4, 6.4, 6.7, 9.2]

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s working again! However it wouldn’t work if Swift isn’t able to infer the type of the array.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let emptyInt = []
genericQuickSort(emptyInt)
// error: &apos;NSArray&apos; is not convertible to &apos;[_]&apos;; did you mean to use &apos;as!&apos; to force downcast?

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can fix it by explicitly defining the type:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let emptyInt: [Int] = []
quickSort(emptyInt) // []

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now Swift compiler wouldn’t complain and will return an empty array as expected!&lt;/p&gt;
&lt;h1&gt;Comparable conformance&lt;/h1&gt;
&lt;p&gt;Our generic quickSort() will be able to sort anything that conforms to &lt;code&gt;Comparable&lt;/code&gt; protocol. But it wouldn’t be able to sort a custom struct. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Dog {
  let id: Int
  let name: String
}

let dog1 = Dog(id: 19123, name: &quot;Bella&quot;)
let dog2 = Dog(id: 26234, name: &quot;Ollie&quot;)
let dog3 = Dog(id: 31241, name: &quot;Klue&quot;)

quickSort([dog2, dog1, dog3])
// error: cannot convert value of type &apos;[Dog]&apos; to expected argument type &apos;[_]&apos;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This happens because Swift doesn’t know how to sort a custom struct that doesn’t conform to &lt;code&gt;Comparable&lt;/code&gt;. How do we fix it? By conforming it to &lt;code&gt;Comparable&lt;/code&gt; of course!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Dog: Comparable { // 1
  // definition
}

func &amp;lt; (l: Dog, r: Dog) -&amp;gt; Bool { // 2
  return l.id &amp;lt; r.id
}

func == (l: Dog, r: Dog) -&amp;gt; Bool { // 3
  return l.id == r.id
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Conforming to &lt;code&gt;Comparable&lt;/code&gt; is as easy as 1-2-3&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Conform the custom class or struct to &lt;code&gt;Comparable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Implement the &lt;code&gt;&amp;lt;&lt;/code&gt; operation by comparing a property of the struct that already is comparable&lt;/li&gt;
&lt;li&gt;Do the same thing with &lt;code&gt;==&lt;/code&gt; operation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Step 2 and 3 can be as simple as comparing ID to as complex as comparing all of the properties in the struct. It depends on your needs.&lt;/p&gt;
&lt;p&gt;Now let’s test it again&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;quickSort([dog2, dog1, dog3])
// [Dog(id: 19123, name: &quot;Bella&quot;), Dog(id: 26234, name: &quot;Ollie&quot;), Dog(id: 31241, name: &quot;Klue&quot;)]

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s working!&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;It is very easy to implement the Swift version of recursive quicksort with generics. The type inference system helps us a lot.&lt;/p&gt;
&lt;h1&gt;Where to go from here?&lt;/h1&gt;
&lt;p&gt;Now that you can sort a randomized array, how about implementing a recursive binary sort with Swift?&lt;/p&gt;
&lt;p&gt;I will post the answer on future blog post.&lt;/p&gt;
</content:encoded></item><item><title>How to Host Static Websites Like Hugo on CentOS VPS with Nginx - Tutorial Part 1</title><link>https://www.augusteo.com/blog/hugo-centos/</link><guid isPermaLink="true">https://www.augusteo.com/blog/hugo-centos/</guid><description>How to Host Static Websites Like Hugo on CentOS VPS with Nginx - Tutorial Part 1 - a blog post by Victor Augusteo</description><pubDate>Sat, 06 Aug 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last time, we have learned how to &lt;a href=&quot;https://hugo.augusteo.com/super-quick-guide-to-start-using-hugo/&quot;&gt;get started with Hugo&lt;/a&gt; and run the server on our local machine. That alone is fun, but our friends couldn’t check it out. What’s the use of a blog if only you can read it? The blog need to be served on the internet through a domain name so people can read it easily.&lt;/p&gt;
&lt;p&gt;In this tutorial, we will explore the delighful ways of hosting Hugo blog with CentOS, Nginx and side of Let’s Encrypt.&lt;/p&gt;
&lt;h1&gt;Contents&lt;/h1&gt;
&lt;p&gt;The steps that we will go through are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spinning up CentOS VPS instance&lt;/li&gt;
&lt;li&gt;Pointing your domain name to the new VPS&lt;/li&gt;
&lt;li&gt;Creating user account and setting up passwordless SSH login&lt;/li&gt;
&lt;li&gt;Installing NginX and setting up site config&lt;/li&gt;
&lt;li&gt;Writing deployment script with Rsync&lt;/li&gt;
&lt;li&gt;Setting up HTTPS with free SSL cert from Let’s Encrypt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The contents will be spread out to several tutorial post to make it easier for you to digest.&lt;/p&gt;
&lt;h1&gt;VPS&lt;/h1&gt;
&lt;p&gt;We will be using &lt;a href=&quot;http://www.vultr.com/?ref=6933377-3B&quot;&gt;Vultr&lt;/a&gt; to host our VPS. Why Vultr? I came highly recommended from my friend &lt;a href=&quot;https://www.centosblog.com/author/curtis/&quot;&gt;Curtis over at CentOS Blog&lt;/a&gt;. He is the devops guy at PwC and we used to work together, so I know to just trust his words on server stuff. Who better to ask from than the guy who play with server all day long?&lt;/p&gt;
&lt;p&gt;Plus if you register to &lt;a href=&quot;http://www.vultr.com/?ref=6933377-3B&quot;&gt;Vultr&lt;/a&gt; with this link, you will get $20 free credit that you can use to spin up server for free!&lt;/p&gt;
&lt;h1&gt;Creating account and spinning up VPS on Vultr&lt;/h1&gt;
&lt;p&gt;So lets over to &lt;a href=&quot;http://www.vultr.com/?ref=6933377-3B&quot;&gt;Vultr&lt;/a&gt; and create a new account if you haven’t already. It is pretty straightforward and should be pretty standard account creation process.&lt;/p&gt;
&lt;p&gt;After you are done, please log in. You should see this screen:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/hugo-centos/hugo-centos-Screenshot_2016-08-07_12_17_59.jpg&quot; alt=&quot;hugo-centos-Screenshot_2016-08-07_12_17_59.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Let’s click the big blue plus button to spin up a new VPS instance. It should say &lt;code&gt;Deploy New Server&lt;/code&gt; when you hover over it. Next screen will ask you series of questions regarding your new server:&lt;/p&gt;
&lt;p&gt;On the top, select &lt;code&gt;Compute instance&lt;/code&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Location&lt;/strong&gt;: Select the one close to you or your readers for better latency&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server Type&lt;/strong&gt;: Select CentOS 7 x64&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server Size&lt;/strong&gt;: Select the $5 option as it should be enough for our needs now. If you start getting millions of users, then we can easily scale up.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Additional Feature&lt;/strong&gt;: You can check the IPv6 and leave the rest unchecked, they are all optional.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Startup Script&lt;/strong&gt;: Leave it empty&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSH Keys&lt;/strong&gt;: Add your RSA public key here or just leave it empty if you don’t know what it means&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server Hostname &amp;amp; Label&lt;/strong&gt;: Enter your domain name on the first field or just leave it blank if you don’t have it now&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All done, now let’s just press the &lt;code&gt;Deploy Now&lt;/code&gt; button on the bottom.&lt;/p&gt;
&lt;p&gt;You should be taken back to the dashboard with your list of server. The new server’s status will be shown as &lt;code&gt;Installing&lt;/code&gt; for a couple minute. Just wait for it until it says &lt;code&gt;Running&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That’s great! Now we have a server running with its very own IP address. It’s yours. Unique. The only one in the world.&lt;/p&gt;
&lt;p&gt;But we don’t have anything on the server yet, so if you tried to open the IP address on browser or curl, you won’t get anything back&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;➜  ~ curl 45.32.190.53
curl: (7) Failed to connect to 45.32.190.53 port 80: Connection refused

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Don’t worry, once we set up NginX on the server, everything will be fine.&lt;/p&gt;
&lt;h1&gt;Registering domain and pointing it to our server&lt;/h1&gt;
&lt;p&gt;I have just registered &lt;code&gt;augusteo.xyz&lt;/code&gt; for this tutorial because it is the cheapest TLD I could find: only AUD 0.50. This tutorial will not cover the process of domain registration and I will assume you have your own domain name to use.&lt;/p&gt;
&lt;p&gt;Let’s login to your domain registrar management page. The example shown is specific to &lt;a href=&quot;https://www.crazydomains.com.au/&quot;&gt;CrazyDomain&lt;/a&gt; as it is where I register my .xyz domain name, but it will be easily applicable to any other registrar.&lt;/p&gt;
&lt;p&gt;Find the option to change/edit your domain’s nameserver:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/hugo-centos/hugo-centos-Account_Manager.jpg&quot; alt=&quot;hugo-centos-Account_Manager.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Change the first and second entry to:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ns1.vultr.com
ns2.vultr.com

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And hit save.&lt;/p&gt;
&lt;p&gt;Now lets get back to &lt;a href=&quot;http://www.vultr.com/?ref=6933377-3B&quot;&gt;Vultr&lt;/a&gt; management page and select &lt;code&gt;DNS&lt;/code&gt; on the top menu bar.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/hugo-centos/hugo-centos-DNS_-_Vultr_com.jpg&quot; alt=&quot;hugo-centos-DNS_-_Vultr_com.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Let’s press the &lt;code&gt;Add Domain&lt;/code&gt; button. In the &lt;code&gt;Domain&lt;/code&gt; field, just enter your domain name. In my case I would enter &lt;code&gt;augusteo.xyz&lt;/code&gt;. In the IP field, enter the IP address of your Vultr VPS instance. You can get it from the &lt;code&gt;Instances&lt;/code&gt; list.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/hugo-centos/hugo-centos-My_Subscriptions_-_Vultr_com.jpg&quot; alt=&quot;hugo-centos-My_Subscriptions_-_Vultr_com.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When you are happy, just press &lt;code&gt;Add&lt;/code&gt;. You should see the finished DNS entry page:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/hugo-centos/hugo-centos-augusteo_xyz_-_DNS_-_Vultr_com.jpg&quot; alt=&quot;hugo-centos-augusteo_xyz_-DNS-_Vultr_com.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We are all done with domain! Now if you access &lt;code&gt;augusteo.xyz&lt;/code&gt; it should point to your new Vultr VPS server. The DNS propagation could take up to 24 until servers around the world knows where to point it to. You can check the propagation using &lt;a href=&quot;https://www.whatsmydns.net/#NS/augusteo.xyz&quot;&gt;What’s my DNS&lt;/a&gt; web app. While it is propagating, we can move on to setting up our server.&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;In this tutorial, we have learned how to spin up a new CentOS VPS and setting up DNS to point domain name to that VPS.&lt;/p&gt;
&lt;p&gt;The next tutorial will go through:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Secure your CentOS server&lt;/li&gt;
&lt;li&gt;Create your new user account&lt;/li&gt;
&lt;li&gt;Adding RSA key for passwordless login&lt;/li&gt;
&lt;li&gt;Installing NginX and setting up configs&lt;/li&gt;
&lt;li&gt;Installing Let’s Encrypt Free SSL certs&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Stay tuned!&lt;/p&gt;
</content:encoded></item><item><title>How To Run Pokemon Go Bot On The Cloud With AWS EC2</title><link>https://www.augusteo.com/blog/pokemon-ec2/</link><guid isPermaLink="true">https://www.augusteo.com/blog/pokemon-ec2/</guid><description>How To Run Pokemon Go Bot On The Cloud With AWS EC2 - a blog post by Victor Augusteo</description><pubDate>Tue, 26 Jul 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Pokemon Go (PoGo) bots today works really well. It can run around spinning Pokestops and catch Pokemons for you automatically. You just need to tell them what to do.&lt;/p&gt;
&lt;p&gt;The most popular PoGo bot right now is the &lt;a href=&quot;https://github.com/NecronomiconCoding/NecroBot&quot;&gt;Necro Bot&lt;/a&gt;. It only runs on windows so those of you who doesn’t have a Windows machine are kind of screwed.&lt;/p&gt;
&lt;p&gt;Or are you?&lt;/p&gt;
&lt;p&gt;There are three ways to run this bot:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get a Windows machine and run it there 24⁄7 which might not be very helpful to your electricity bill&lt;/li&gt;
&lt;li&gt;Run it on a Windows VM on your Mac or Linux machine&lt;/li&gt;
&lt;li&gt;Run it on VPS such as &lt;a href=&quot;https://aws.amazon.com/ec2/&quot;&gt;AWS EC2&lt;/a&gt; which runs all the time, accessible everywhere and very affordable (even free!)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I actually have a gaming PC with Windows, but it is too overpowered to just run this small app 24 hours a day. So let’s do it on Amazon’s server!&lt;/p&gt;
&lt;h1&gt;What do you need?&lt;/h1&gt;
&lt;p&gt;This guide is mostly for people who wants to run the bot on AWS EC2. It is totally fine to run it on your windows machine if you didn’t want to bother with AWS. Just go to the &lt;a href=&quot;https://github.com/NecronomiconCoding/NecroBot/wiki/Compiled-Releases&quot;&gt;NecroBot wiki&lt;/a&gt; and follow the instruction there.&lt;/p&gt;
&lt;p&gt;If you have never used AWS before. Great news, you can do all this for free. Amazon has the free trial for the first year for new customers. You could also just register a new email and get the free offer again.&lt;/p&gt;
&lt;p&gt;So please head to &lt;a href=&quot;https://aws.amazon.com/&quot;&gt;AWS website&lt;/a&gt; to register or login.&lt;/p&gt;
&lt;h1&gt;Launching EC2&lt;/h1&gt;
&lt;p&gt;Once you inside the console, please click on Services -&amp;gt; EC2 on the menu at the top.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/pokemon-ec2/pokemon-ec2-ec2img.jpg&quot; alt=&quot;pokemon-ec2-ec2img.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Let’s press the &lt;code&gt;Launch Instance&lt;/code&gt; button to start. Scroll down until you see the one called:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Microsoft Windows Server 2012 R2 Base&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/pokemon-ec2/pokemon-ec2-ec2windowsimg.jpg&quot; alt=&quot;pokemon-ec2-ec2windowsimg.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;And click on &lt;code&gt;Select&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;On the next screen, let’s go with the free tier one. Then please press &lt;code&gt;Review and Launch&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/pokemon-ec2/pokemon-ec2-ec2freeimg.jpg&quot; alt=&quot;pokemon-ec2-ec2freeimg.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Then just press &lt;code&gt;Launch&lt;/code&gt; when you are ready.&lt;/p&gt;
&lt;p&gt;When prompted for &lt;code&gt;key pair&lt;/code&gt;, select &lt;code&gt;Create a new key pair&lt;/code&gt; and give it a memorable name, such as &lt;code&gt;myKey&lt;/code&gt;. Download the key and press &lt;code&gt;Launch Instance&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/pokemon-ec2/pokemon-ec2-ec2keyimg.jpg&quot; alt=&quot;pokemon-ec2-ec2keyimg.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;That’s it. Your first cloud server is being initialized and will be ready in several minutes.&lt;/p&gt;
&lt;p&gt;Click on &lt;code&gt;Services&lt;/code&gt; button at the top and select &lt;code&gt;EC2&lt;/code&gt; again. This time you should see &lt;code&gt;1 Running Instances&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Click on that link and you will see one server listed on the table.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/pokemon-ec2/pokemon-ec2-ec2list.jpg&quot; alt=&quot;pokemon-ec2-ec2list.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If the &lt;code&gt;Status Checks&lt;/code&gt; still says &lt;code&gt;Initializing&lt;/code&gt; then you just got to wait until it’s ready.&lt;/p&gt;
&lt;p&gt;When it is ready, the text should change into &lt;code&gt;2/2 checks...&lt;/code&gt; with a green tick. It’s time to RDP into it!&lt;/p&gt;
&lt;h1&gt;Getting login details&lt;/h1&gt;
&lt;p&gt;Now please click on the only server you have and click the &lt;code&gt;Connect&lt;/code&gt; button at the top.&lt;/p&gt;
&lt;p&gt;Click on &lt;code&gt;Download Remote Desktop File&lt;/code&gt; to get the &lt;code&gt;.rdp&lt;/code&gt; file then click on &lt;code&gt;Get Password&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In the next screen, click on &lt;code&gt;Choose file&lt;/code&gt; and select the &lt;code&gt;myKey.pem&lt;/code&gt; file that you downloaded earlier before launching the instance.&lt;/p&gt;
&lt;p&gt;Once the key is populated, just click &lt;code&gt;Decrypt Password&lt;/code&gt;. Now write down this password as you will need it to login to your instance.&lt;/p&gt;
&lt;h1&gt;Remote access into the instance&lt;/h1&gt;
&lt;p&gt;If you are on a mac, you will have to download the app &lt;a href=&quot;https://itunes.apple.com/au/app/microsoft-remote-desktop/id715768417?mt=12&quot;&gt;Microsoft Remote Desktop&lt;/a&gt; from the Mac App Store.&lt;/p&gt;
&lt;p&gt;On windows, you should have the &lt;code&gt;Remote Desktop Connection&lt;/code&gt; app installed already.&lt;/p&gt;
&lt;p&gt;Now please run the &lt;code&gt;.rdp&lt;/code&gt; file that you downloaded on the previous step. It should run the RDP app and logs you into the server. Enter the password you get before and you are ready to proceed!&lt;/p&gt;
&lt;p&gt;If this is your first time doing RDP, just treat the remote desktop like any other Windows machine. Pretty neat huh?&lt;/p&gt;
&lt;h1&gt;Downloading the bot&lt;/h1&gt;
&lt;p&gt;From the inside of your remote desktop, just click the start button and select Internet Explorer. You should see the warning about enhanced security configs. This means that IE will prompt you if you really wanted to access a webpage for your safety. So when you are prompted, just click &lt;code&gt;Accept&lt;/code&gt; or &lt;code&gt;Add&lt;/code&gt; to enable access.&lt;/p&gt;
&lt;p&gt;Now let’s go to the &lt;a href=&quot;https://github.com/NecronomiconCoding/NecroBot/wiki/Compiled-Releases&quot;&gt;NecroBot Installation Wiki&lt;/a&gt; and read the instructions. When you understand what to do, lets click on the &lt;code&gt;Release.zip&lt;/code&gt; link.&lt;/p&gt;
&lt;p&gt;On the next page, just click on the first &lt;code&gt;Release.zip&lt;/code&gt; under &lt;code&gt;Downloads&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you get an error message saying &lt;code&gt;Your current security settings do not allow this file to be downloaded.&lt;/code&gt; then just click the gear icon and go to &lt;code&gt;Internet Options&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/pokemon-ec2/pokemon-ec2-internetoptions.jpg&quot; alt=&quot;pokemon-ec2-internetoptions.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;From there go to &lt;code&gt;Security&lt;/code&gt; tab and select &lt;code&gt;Custom Level&lt;/code&gt; and scroll down until you see the disabled download. Just switch it to enable.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/pokemon-ec2/pokemon-ec2-enabledownload.jpg&quot; alt=&quot;pokemon-ec2-enabledownload.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now try to download the file again and you should be able to. Once it is done, just click on &lt;code&gt;Open Folder&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;There you have it, the bot is extracted and ready to be configured. Just follow the steps outlined in the &lt;a href=&quot;https://github.com/NecronomiconCoding/NecroBot/wiki/Compiled-Releases&quot;&gt;NecroBot wiki&lt;/a&gt; and you should be on your way to leveling up!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/pokemon-ec2/pokemon-ec2-bot.jpg&quot; alt=&quot;pokemon-ec2-bot.jpg&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;Multiple bots!&lt;/h1&gt;
&lt;p&gt;You can run multiple bots using the same windows instance. Just make a copy of the &lt;code&gt;Release&lt;/code&gt; folder and change the login details. In no time you will be running hundreds of bots!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/pokemon-ec2/pokemon-ec2-multiple.jpg&quot; alt=&quot;pokemon-ec2-multiple.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;So that’s it. Good luck and let me know if you have any questions!&lt;/p&gt;
</content:encoded></item><item><title>Super Quick Guide to Start Using Hugo</title><link>https://www.augusteo.com/blog/hugo-quick-start/</link><guid isPermaLink="true">https://www.augusteo.com/blog/hugo-quick-start/</guid><description>Super Quick Guide to Start Using Hugo - a blog post by Victor Augusteo</description><pubDate>Sun, 24 Jul 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Wordpress is still the defacto CMS for blogging or just any simple website generally. I’ve created many Wordpress site both for clients and myself. With the plethora of plugins, it can do pretty much anything from e-commerce to paid-membership site.&lt;/p&gt;
&lt;p&gt;So after all, why would you choose to change?&lt;/p&gt;
&lt;p&gt;Before I knew about static site generators, the choice was between using CMS of some kind versus running straight up html/js/css. The former has a lot of advantages, but it is heavier and requires more maintenance. The latter is lighter, easier to run, but requires a lot of technical know how to code them.&lt;/p&gt;
&lt;h1&gt;Static site generator&lt;/h1&gt;
&lt;p&gt;The static site generator sits happily in the middle of those two. It is surprisingly easy to use and quite delightful, suprisingly.&lt;/p&gt;
&lt;p&gt;There are many choices to explore and you can have a look at them at &lt;a href=&quot;https://www.staticgen.com/&quot;&gt;StaticGen&lt;/a&gt;. The choices really just boils down to preference as most of them works really well.&lt;/p&gt;
&lt;p&gt;I chose &lt;a href=&quot;https://gohugo.io/&quot;&gt;Hugo&lt;/a&gt; mainly because it uses Go language instead of Ruby or JS like the others. Nothing wrong with those, it’s just that I wanted to try out new things.&lt;/p&gt;
&lt;p&gt;There is a really good &lt;a href=&quot;http://gohugo.io/overview/quickstart/&quot;&gt;getting started guide&lt;/a&gt; by the folks at Hugo and I will just go through several of the most useful functions that it has.&lt;/p&gt;
&lt;h1&gt;Installation&lt;/h1&gt;
&lt;p&gt;It is very easy to install hugo if you have Brew on your mac. Just run this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;brew update &amp;amp;&amp;amp; brew install hugo&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;After a while, you will be ready to go!&lt;/p&gt;
&lt;h1&gt;Initiating a new site&lt;/h1&gt;
&lt;p&gt;After installation, just &lt;code&gt;cd&lt;/code&gt; to where you want your new project to be and run:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;hugo new site {name of site}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;It will produce a list of files to start.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/hugo-quick-start/hugo-quick-start-hugonewsite.jpg&quot; alt=&quot;hugo-quick-start-hugonewsite.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We will be mainly playing with themes and config.&lt;/p&gt;
&lt;h1&gt;Installing theme&lt;/h1&gt;
&lt;p&gt;From the main folder, just do &lt;code&gt;cd themes&lt;/code&gt; to move into themes directory and install any themes that you want. You can either choose one from the &lt;a href=&quot;http://themes.gohugo.io/&quot;&gt;GoHugo theme list&lt;/a&gt; or you can clone &lt;a href=&quot;https://github.com/augusteo/ghostwriter&quot;&gt;my theme&lt;/a&gt; by running:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git clone https://github.com/augusteo/ghostwriter&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I couldn’t find a theme that I really liked so I tinkered one out. This theme is combination of &lt;a href=&quot;http://themes.gohugo.io/ghostwriter/&quot;&gt;Ghostwriter&lt;/a&gt; and &lt;a href=&quot;http://themes.gohugo.io/cactus/&quot;&gt;Cactus&lt;/a&gt; theme with several twist to make it cleaner and more minimalist.&lt;/p&gt;
&lt;p&gt;after the theme is done, just edit the &lt;code&gt;config.toml&lt;/code&gt; file to include the line&lt;/p&gt;
&lt;p&gt;&lt;code&gt;theme = &quot;ghostwriter&quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;and you are done!&lt;/p&gt;
&lt;h1&gt;Writing a blogpost&lt;/h1&gt;
&lt;p&gt;This is my favourite part of static site gen. No longer do we need to go to the web browser or a specialized writer to start blogging. In fact I am writing with Vim now!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/hugo-quick-start/hugo-quick-start-hugovim.jpg&quot; alt=&quot;hugo-quick-start-hugovim.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Markdown syntax that Hugo supports are a lot simpler compared to HTML tags and a lot faster to write.&lt;/p&gt;
&lt;h1&gt;Publishing&lt;/h1&gt;
&lt;p&gt;After writing your blogpost, depending on the theme, you might need to call &lt;a href=&quot;https://gohugo.io/commands/hugo_undraft/&quot;&gt;undraft&lt;/a&gt; to make it visible on your Hugo site.&lt;/p&gt;
&lt;p&gt;Once you make sure that they are undrafted, just run &lt;code&gt;hugo server&lt;/code&gt; on the main directory and you should see the server running.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/hugo-quick-start/hugo-quick-start-hugoserver.jpg&quot; alt=&quot;hugo-quick-start-hugoserver.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now when you open &lt;code&gt;localhost:1313&lt;/code&gt; on your web browser, you should see your new blog displayed!&lt;/p&gt;
&lt;h1&gt;Deploying&lt;/h1&gt;
&lt;p&gt;I will write an article soon about spinning up a new CentOS VPS, installing Nginx and writing a script to automate deployment with Rsync and SSH. In the future we will also explore hosting the site on AWS S3 and CloudFront.&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;Hugo and the other static site generators does what it promises. By serving static files instead of generated pages and database fetches, you will save a lot of server cost in the long run. The more traffic you have, the more money you will save. Moreover, since static files doesn’t require processing at all, it will be served at a much faster speed compared to traditional CMS.&lt;/p&gt;
&lt;p&gt;The best benefit in my opinion is that you can write markdown anywhere, edit and publish your articles without touching a web server.&lt;/p&gt;
&lt;p&gt;By enabling CI or deployment script, getting your writing to the internet has never been easier!&lt;/p&gt;
</content:encoded></item><item><title>How To Simulate GPS Movement in iOS</title><link>https://www.augusteo.com/blog/simulate-gps/</link><guid isPermaLink="true">https://www.augusteo.com/blog/simulate-gps/</guid><description>How To Simulate GPS Movement in iOS - a blog post by Victor Augusteo</description><pubDate>Sun, 10 Jul 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Be warned: doing this on Pokemon Go might get your account suspended for cheating.&lt;/p&gt;
&lt;h1&gt;Motivation&lt;/h1&gt;
&lt;p&gt;Since the release of Pokemon Go, the world has went insane catching those cute monsters. Myself included. Its all fun and games until you realize that if you live in the rural area, there’s not much that you can do.&lt;/p&gt;
&lt;p&gt;I live 25km away from Melbourne CBD and there are two Pokestops within 5km of my house. Compare this to the CBD where there are hundreds of Pokestops and Gym everywhere. It’s ridiculous.&lt;/p&gt;
&lt;p&gt;How about needing to walk 10km to hatch one Poke egg? I understand that it is good to have a walk around, but in the cold rainy winter day, I’d prefer to stay home.&lt;/p&gt;
&lt;p&gt;There must be a better way to enjoy this game than driving 25km to city just to play. Right?&lt;/p&gt;
&lt;h1&gt;Solution&lt;/h1&gt;
&lt;p&gt;We are going to use the Xcode debugger to simulate GPS location and catch all the Pokemon!&lt;/p&gt;
&lt;h2&gt;Preparation&lt;/h2&gt;
&lt;p&gt;– very basic iOS development experience
– Mac machine with Xcode installed (it’s free)
– iPhone with Pokemon Go installed
– Apple developer account to run app on your phone
– text editor with regex find and replace function, such as &lt;a href=&quot;https://www.sublimetext.com/&quot;&gt;Sublime Text&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Steps&lt;/h2&gt;
&lt;p&gt;– draw the map that your trainer will walk through
– process the map file into .gpx file that Xcode will accept with regex
– create iPhone app that will be used to inject the .gpx file
– catch ‘em all!&lt;/p&gt;
&lt;p&gt;Now let’s get started&lt;/p&gt;
&lt;h1&gt;Drawing the map&lt;/h1&gt;
&lt;p&gt;Lets visit this website first: &lt;a href=&quot;http://www.gpsies.com/createTrack.do&quot;&gt;GPSies track creator&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Then put &lt;code&gt;3&lt;/code&gt; in &lt;code&gt;Speed (mph)&lt;/code&gt; field to simulate average walking speed. If you wanted to test the limit, I’ve tried 6 mph and it still counts for hatching eggs.&lt;/p&gt;
&lt;p&gt;After that, search for the big city that you wanted to catch your Pokemon in. In my case, I entered &lt;code&gt;Melbourne&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/simulate-gps/simulate-gps-Screen-Shot-2016-07-10-at-5.51.59-PM.png&quot; alt=&quot;simulate-gps-Screen-Shot-2016-07-10-at-5.51.59-PM.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Zoom in until you can see the roads clearly and you can start clicking to draw waypoints. I’d recommend to draw around 5-10km worth of waypoints to reduce repetition and looping. The longer the better.&lt;/p&gt;
&lt;p&gt;This is my track:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/simulate-gps/simulate-gps-Screen-Shot-2016-07-10-at-5.58.59-PM.png&quot; alt=&quot;simulate-gps-Screen-Shot-2016-07-10-at-5.58.59-PM.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After we are done with drawing tracks, scroll down and find the &lt;code&gt;Export file&lt;/code&gt; section and press &lt;code&gt;download&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/simulate-gps/simulate-gps-Screen-Shot-2016-07-10-at-6.01.21-PM.png&quot; alt=&quot;simulate-gps-Screen-Shot-2016-07-10-at-6.01.21-PM.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Congrats, you have done drawing your map and exporting it!&lt;/p&gt;
&lt;h1&gt;Processing the gpx file&lt;/h1&gt;
&lt;p&gt;Now open the .gpx file that just been downloaded with Sublime Text. It should look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;gpx xmlns=&quot;http://www.topografix.com/GPX/1/1&quot; xmlns:gpsies=&quot;http://www.gpsies.com/GPX/1/0&quot; creator=&quot;GPSies http://www.gpsies.com - GPSies Track&quot; version=&quot;1.1&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xsi:schemaLocation=&quot;http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.gpsies.com/GPX/1/0 http://www.gpsies.com/gpsies.xsd&quot;&amp;gt;
  &amp;lt;metadata&amp;gt;
    &amp;lt;name&amp;gt;GPSies Track&amp;lt;/name&amp;gt;
    &amp;lt;link href=&quot;http://www.gpsies.com/&quot;&amp;gt;
      &amp;lt;text&amp;gt;GPSies Track on GPSies.com&amp;lt;/text&amp;gt;
    &amp;lt;/link&amp;gt;
    &amp;lt;time&amp;gt;2016-07-10T05:47:48Z&amp;lt;/time&amp;gt;
  &amp;lt;/metadata&amp;gt;
  &amp;lt;trk&amp;gt;
    &amp;lt;name&amp;gt;GPSies Track on GPSies.com&amp;lt;/name&amp;gt;
      &amp;lt;trkseg&amp;gt;
        &amp;lt;trkpt lat=&quot;-37.8117843&quot; lon=&quot;144.9566173&quot;&amp;gt;
          &amp;lt;ele&amp;gt;62.000000&amp;lt;/ele&amp;gt;
          &amp;lt;time&amp;gt;2010-01-01T00:00:00Z&amp;lt;/time&amp;gt;
        &amp;lt;/trkpt&amp;gt;
          ...
        &amp;lt;trkpt lat=&quot;-37.8118521&quot; lon=&quot;144.9564456&quot;&amp;gt;
          &amp;lt;ele&amp;gt;59.000000&amp;lt;/ele&amp;gt;
          &amp;lt;time&amp;gt;2010-01-01T03:27:06Z&amp;lt;/time&amp;gt;
        &amp;lt;/trkpt&amp;gt;
      &amp;lt;/trkseg&amp;gt;
  &amp;lt;/trk&amp;gt;
&amp;lt;/gpx&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The problem is, we can’t just use the file like that. Xcode wouldn’t understand it. So lets get pumping!&lt;/p&gt;
&lt;p&gt;First just replace all this part:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;gpx xmlns=&quot;http://www.topografix.com/GPX/1/1&quot; xmlns:gpsies=&quot;http://www.gpsies.com/GPX/1/0&quot; creator=&quot;GPSies http://www.gpsies.com - GPSies Track&quot; version=&quot;1.1&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xsi:schemaLocation=&quot;http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.gpsies.com/GPX/1/0 http://www.gpsies.com/gpsies.xsd&quot;&amp;gt;
  &amp;lt;metadata&amp;gt;
    &amp;lt;name&amp;gt;GPSies Track&amp;lt;/name&amp;gt;
    &amp;lt;link href=&quot;http://www.gpsies.com/&quot;&amp;gt;
      &amp;lt;text&amp;gt;GPSies Track on GPSies.com&amp;lt;/text&amp;gt;
    &amp;lt;/link&amp;gt;
    &amp;lt;time&amp;gt;2016-07-10T05:47:48Z&amp;lt;/time&amp;gt;
  &amp;lt;/metadata&amp;gt;
  &amp;lt;trk&amp;gt;
    &amp;lt;name&amp;gt;GPSies Track on GPSies.com&amp;lt;/name&amp;gt;
    &amp;lt;trkseg&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;gpx&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then at the end of the file, just delete the lines:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    &amp;lt;/trkseg&amp;gt;
  &amp;lt;/trk&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should have something like this now:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;gpx&amp;gt;
  &amp;lt;trkpt lat=&quot;-37.8117843&quot; lon=&quot;144.9566173&quot;&amp;gt;
    &amp;lt;ele&amp;gt;62.000000&amp;lt;/ele&amp;gt;
    &amp;lt;time&amp;gt;2010-01-01T00:00:00Z&amp;lt;/time&amp;gt;
  &amp;lt;/trkpt&amp;gt;
  ...
  &amp;lt;trkpt lat=&quot;-37.8118521&quot; lon=&quot;144.9564456&quot;&amp;gt;
    &amp;lt;ele&amp;gt;59.000000&amp;lt;/ele&amp;gt;
    &amp;lt;time&amp;gt;2010-01-01T03:27:06Z&amp;lt;/time&amp;gt;
  &amp;lt;/trkpt&amp;gt;
&amp;lt;/gpx&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s great! What follows is a bit tricky and not as straightforward. We will have to change the format of the data from this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;trkpt lat=&quot;-37.8117843&quot; lon=&quot;144.9566173&quot;&amp;gt;
  &amp;lt;ele&amp;gt;62.000000&amp;lt;/ele&amp;gt;
  &amp;lt;time&amp;gt;2010-01-01T00:00:00Z&amp;lt;/time&amp;gt;
&amp;lt;/trkpt&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;into this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;wpt lat=&quot;-37.8117843&quot; lon=&quot;144.9566173&quot;&amp;gt;&amp;lt;time&amp;gt;2010-01-01T00:00:00Z&amp;lt;/time&amp;gt;&amp;lt;/wpt&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Doing them one by one is possible, but it takes a lot of time to say the least when you have hundreds of points. To make things simpler for us, let’s use &lt;a href=&quot;https://en.wikipedia.org/wiki/Regular_expression&quot;&gt;Regular Expression or Regex&lt;/a&gt;. If you have never used Regex before, it is just a language rules that we can use to select, modify, extract or otherwise manipulate texts.&lt;/p&gt;
&lt;p&gt;Now inside Sublime Text, open up the &lt;code&gt;Find and Replace&lt;/code&gt; menu by pressing &lt;code&gt;cmd+shift+f&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Click on the leftmost icon with a dot and asterisk symbol on it. There should be a popup saying &lt;code&gt;Regular Expression&lt;/code&gt; when you hover over it.&lt;/p&gt;
&lt;p&gt;Then in the &lt;code&gt;Find&lt;/code&gt; field enter: &lt;code&gt;\s+&amp;lt;trkpt\s(.+)\n.+\n\s+(.+)\n.+&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And in the &lt;code&gt;Replace&lt;/code&gt; field enter: &lt;code&gt;\n&amp;lt;wpt $1$2&amp;lt;/wpt&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You should have it looking like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/simulate-gps/simulate-gps-Screen-Shot-2016-07-10-at-6.27.54-PM-1024x136.png&quot; alt=&quot;simulate-gps-Screen-Shot-2016-07-10-at-6.27.54-PM-1024x136.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Then just press Replace and OK when asked. Your waypoints should be transformed into Xcode readable file.&lt;/p&gt;
&lt;p&gt;If you think what happened is totally ridiculous and looked like sorcery, you might be right. You can read the short explanation by going to Regex101, but I won’t go into details here. Regex is big topic by itself and there are many people smarter than me that has covered it in detail.&lt;/p&gt;
&lt;p&gt;Your final gpx file should look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;gpx&amp;gt;
&amp;lt;wpt lat=&quot;-37.8114622&quot; lon=&quot;144.9647283&quot;&amp;gt;&amp;lt;time&amp;gt;2010-01-01T00:00:00Z&amp;lt;/time&amp;gt;&amp;lt;/wpt&amp;gt;
...
&amp;lt;wpt lat=&quot;-37.8116487&quot; lon=&quot;144.9645996&quot;&amp;gt;&amp;lt;time&amp;gt;2010-01-01T01:19:44Z&amp;lt;/time&amp;gt;&amp;lt;/wpt&amp;gt;
&amp;lt;/gpx&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you are ready to make your injector app!&lt;/p&gt;
&lt;h1&gt;Location injection app&lt;/h1&gt;
&lt;p&gt;The purpose of this app is just to inject the gpx file and run in the background while we play Pokemon Go. Thus it must be:
– always be plugged in to the computer with Xcode
– as light as possible to take up less memory
– always run in the background&lt;/p&gt;
&lt;p&gt;The first point is pretty easy.
Second point is also easy since we will just have one empty controller with nothing on it.&lt;/p&gt;
&lt;p&gt;Now the third point is tricky. We can’t just run a new empty app as iOS will kill it after several minutes of no activity to save battery and memory. We have to force it to keep running by using this trick:&lt;strong&gt;keep requesting location update in the background&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;With this two, I was able to keep the app running for several hours in the background while I play Pokemon Go.&lt;/p&gt;
&lt;h2&gt;Project Setup&lt;/h2&gt;
&lt;p&gt;Open Xcode and press &lt;code&gt;cmd+shift+n&lt;/code&gt; to create a new project.
Choose the &lt;code&gt;Single View Application&lt;/code&gt; to start with and enter a name for your app. You can put anything here as it doesn’t matter. For example I named it &lt;code&gt;LocTest&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;After the project is created, we have to do several things:&lt;/p&gt;
&lt;h2&gt;Backgrounding Capabilities&lt;/h2&gt;
&lt;p&gt;Go to the capabilities tab and turn on &lt;code&gt;Background Modes&lt;/code&gt;.
After that, turn on the &lt;code&gt;Location&lt;/code&gt; checklist.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/simulate-gps/simulate-gps-Screen-Shot-2016-07-10-at-6.55.21-PM-1024x397.png&quot; alt=&quot;simulate-gps-Screen-Shot-2016-07-10-at-6.55.21-PM-1024x397.png&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Info.plist&lt;/h2&gt;
&lt;p&gt;Open the &lt;code&gt;Info.plist&lt;/code&gt; on the project navigator and add these two keys in the plist:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/simulate-gps/simulate-gps-Screen-Shot-2016-07-10-at-7.00.26-PM-1024x77.png&quot; alt=&quot;simulate-gps-Screen-Shot-2016-07-10-at-7.00.26-PM-1024x77.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The first line enables the location permission prompt.
Second line just help your app to not get closed during backgrounding.&lt;/p&gt;
&lt;h2&gt;Import files&lt;/h2&gt;
&lt;p&gt;There are two files that we need. One is the processes gpx file that we did previously.&lt;/p&gt;
&lt;p&gt;Drag and drop the two files into your project navigator and select the &lt;code&gt;Copy items if needed&lt;/code&gt; box.&lt;/p&gt;
&lt;h3&gt;Codes to prevent backgrounding&lt;/h3&gt;
&lt;p&gt;Open &lt;code&gt;AppDelegate.swift&lt;/code&gt; and below &lt;code&gt;import UIKit&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import CoreLocation

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make the AppDelegate class conforms to &lt;code&gt;CLLocationManagerDelegate&lt;/code&gt; protocol&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the locationManager property below the window property:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var window: UIWindow?
let locationManager = CLLocationManager()

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add this function into the class:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func location() {
  locationManager.delegate = self
  locationManager.allowsBackgroundLocationUpdates = true
  locationManager.desiredAccuracy = kCLLocationAccuracyBest
  locationManager.requestAlwaysAuthorization()
  locationManager.startUpdatingLocation()
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally just call the function inside &lt;code&gt;didFinishLaunching&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -&amp;gt; Bool {
  location()
  return true
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the app is done!&lt;/p&gt;
&lt;p&gt;Just launch it and you should see a white screen with a popup asking for location permission. Just tap on &lt;code&gt;Allow&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If it doesn’t ask you for permission, just go to&lt;code&gt;Settings -&amp;gt; Privacy -&amp;gt; Location Services -&amp;gt; &apos;your app name&apos; -&amp;gt; Always&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;There are better ways to debug this, but for the purpose of this tutorial, let’s just ignore the fact that we have a bug.&lt;/p&gt;
&lt;h2&gt;Injecting the location file&lt;/h2&gt;
&lt;p&gt;While the app is running, you should see the debugger console on the bottom half of Xcode. It looks like this:&lt;/p&gt;
&lt;p&gt;If it’s not there, press &lt;code&gt;cmd+shift+y&lt;/code&gt; to bring it up.&lt;/p&gt;
&lt;p&gt;Click the arrow button and you should see your gpx file listed:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/simulate-gps/simulate-gps-Screen-Shot-2016-07-10-at-7.31.57-PM.png&quot; alt=&quot;simulate-gps-Screen-Shot-2016-07-10-at-7.31.57-PM.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Click on it and your app is done!&lt;/p&gt;
&lt;h1&gt;Catch ‘em all&lt;/h1&gt;
&lt;p&gt;Now you can go to your iPhone and press home button to background the injector app. It should still be running in the background.&lt;/p&gt;
&lt;p&gt;Open the Pokemon Go app and you should see your trainer walking around your pre-set path, passing through all the Pokestops and Pokemons!&lt;/p&gt;
&lt;p&gt;I hope you enjoy the tutorial and let me know if you got any questions on the comment box below!&lt;/p&gt;
&lt;h1&gt;GPS error&lt;/h1&gt;
&lt;p&gt;Even though we have tried to make the app run in the background, sometimes iOS will still kill it after a long period of time. In my testing this is several hours. You will see an error in Xcode console that says:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Message from debugger: Terminated due to memory issue&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Pokemon Go app will either:
– show GPS error
– reset your GPS to your real location&lt;/p&gt;
&lt;p&gt;When that happens, just rerun the injector app and re-pick the gpx file. You will be good to go again!&lt;/p&gt;
&lt;p&gt;I have not been able to find a more elegant solution that keeps iOS from killing the app. Let me know if you know a better way!&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;You have learned how to:
– create a map waypoint
– parse the gpx with regex
– create app that will stay active in the background by
– playing an empty mp3 file continuously
– continously updates location in the background
– cheat on Pokemon&lt;/p&gt;
&lt;p&gt;Where to go from here?
– create another map path on beaches/port to catch that Magikarp (for Gyarados of course)
– use this technique to test (or cheat) all other GPS based apps
– write a comment in the box below 🙂&lt;/p&gt;
&lt;p&gt;Thanks for reading yo!&lt;/p&gt;
</content:encoded></item><item><title>Software Craftsmanship</title><link>https://www.augusteo.com/blog/software-craftsmanship/</link><guid isPermaLink="true">https://www.augusteo.com/blog/software-craftsmanship/</guid><description>Software Craftsmanship - a blog post by Victor Augusteo</description><pubDate>Sat, 09 Jul 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This post is a companion of my talk for &lt;a href=&quot;http://www.meetup.com/MelbourneMobile/events/228598697/&quot;&gt;Melbourne Mobile Meetup&lt;/a&gt;. You can get the &lt;a href=&quot;https://hugo.augusteo.com/blogFiles/MMMSoftwareCraftsmanship.pdf&quot;&gt;slides here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the early years of software development, large numbers of software projects tends to be over budget and over time. The community then tries to bring in engineering practices to help make software projects predictable. Thus ‘Software Engineering’ was born.&lt;/p&gt;
&lt;p&gt;The problem with engineering is that it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Treats all developers equally as resources or automatons even though we know that developers are vastly different to each others&lt;/li&gt;
&lt;li&gt;Assumes highly degree of control and predictability, but software projects is often unpredictable&lt;/li&gt;
&lt;li&gt;Requires approved body of knowledge, certifications and college degree before you can call yourself engineers. In software, there are many highly achieving developers with only high school diploma and no more&lt;/li&gt;
&lt;li&gt;Generally has end-goal set in advance. For example, a bridge will have to connect point a to point b. The two point will be unlikely to change. Contrast this with software project that changes on daily basis&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Craftsmanship&lt;/h1&gt;
&lt;p&gt;Craftsmanship places the craftsman in the center of the process.&lt;/p&gt;
&lt;p&gt;Craftsman are people who:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dedicates their working life to getting better at their craft&lt;/li&gt;
&lt;li&gt;Learns their skills through apprenticeship to a master craftsman&lt;/li&gt;
&lt;li&gt;Aims for mastery of the craft&lt;/li&gt;
&lt;li&gt;Teaches others through apprenticeship&lt;/li&gt;
&lt;li&gt;Builds a reputation based on what they have delivered and not what exams they have passed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Passion is often a positive modifier for craftsman. I argue that passion is optional as you could succeed in a craft without passion although it definitely help.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But with the rise of industrialization there are fewer and fewer craftsmen. One of the biggest remaining groups is computer programmers.&lt;/p&gt;
&lt;p&gt;~ Paul Graham&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;Software Craftsmanship&lt;/h1&gt;
&lt;p&gt;The concept isn’t new. The classics embraces it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/Pragmatic-Programmer-Journeyman-Master/dp/020161622X&quot;&gt;Pragmatic Programmer&lt;/a&gt; comes with ‘Journeyman to Master’ subtitle, which reflects the classic craftsmanship journey.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/Software-Craftsmanship-Imperative-Pete-McBreen/dp/0201733862&quot;&gt;Software Craftsmanship: The New Imperative&lt;/a&gt; talked about the situation that our profession is facing right now. “As the demand for software has exploded, the software engineering establishment has attempted to adapt to the changing times with short training programs that teach the syntax of coding languages. But writing code is no longer the hard part of development; the hard part is figuring out what to write. This kind of know-how demands a skilled craftsman, not someone who knows only how to pass a certification course.” still true 15 years after the book is published.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/Apprenticeship-Patterns-Guidance-Aspiring-Craftsman/dp/0596518382&quot;&gt;Apprenticeship Pattern&lt;/a&gt; is similar to the design patterns and refactoring books. it goes through many patterns that we can follow on the path to mastery.&lt;/p&gt;
&lt;p&gt;There is also a sequel to the Agile Manifesto, aptly titled Manifesto for Software Craftsmanship where you can sign and list your name on the page.&lt;/p&gt;
&lt;h2&gt;Stages of Craftsmanship&lt;/h2&gt;
&lt;p&gt;We all started as a Novice in a craft. As we pick up a few skills, we become Initiate. Once an initiate feels she wanted to pursue the craft, she should find a master to apprentice to.&lt;/p&gt;
&lt;h3&gt;Apprentice&lt;/h3&gt;
&lt;p&gt;In the medieval Europe, an apprentice who wants to learn new craft will have to look for a master to study from. Thankfully for us, with the advent of the internet and printing press, it’s easy to learn from the masters of the craft through books, videos and courses.&lt;/p&gt;
&lt;p&gt;In modern times, I consider university student to be on apprenticeship stage. Just starting their craftsmanship journey, learning from the professors.&lt;/p&gt;
&lt;h3&gt;Journeyman&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;“The journeyman is focused on building an ever-larger portfolio of applications that demonstrates his progress in the craft; he moves between projects and masters, seeking to diversify and deepen his portfolio; he seeks to elevate his status within the community; and he strives to become ready to be a master.”&lt;/p&gt;
&lt;p&gt;~ Apprenticeship Patterns, p. 31&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That quote perfectly describes this stage.&lt;/p&gt;
&lt;h3&gt;Master&lt;/h3&gt;
&lt;p&gt;In the old days, a expert journeyman has to produce a ‘Masterpiece’ that has to be accepted by other masters before allowed to join the guild.&lt;/p&gt;
&lt;p&gt;In software craftsmanship, this would be the people who has mastered their craft, produced masterpieces and shared their superior skills to advance their professions and the wider world. There is no certification for it. We can’t just copy someone else’s masterpiece either, because you have to create your own.&lt;/p&gt;
&lt;p&gt;On scale of 1-10, on a good day, you might rate yourself as 8s or 9s. but once you see the works of the masters, you realize the scales goes up to 100s or 1000s.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Linus_Torvalds&quot;&gt;Linus Torvalds&lt;/a&gt; is one of those people. his masterpiece is, of course, Linux and Git which changed the whole computing landscape forever.&lt;/p&gt;
&lt;p&gt;My other favourite masters are &lt;a href=&quot;https://en.wikipedia.org/wiki/Chris_Lattner&quot;&gt;Chris Lattner&lt;/a&gt; for creating LLVM, CLang, and Swift; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Richard_Stallman&quot;&gt;Richard Stallman&lt;/a&gt; for creating the GNU projects &amp;amp; the Free Software Foundation that started the open-source movement.&lt;/p&gt;
&lt;p&gt;There are many other masters that has directly impacted our lives through their masterpieces, and all of us should aspire to do better than them.&lt;/p&gt;
&lt;h2&gt;Growing&lt;/h2&gt;
&lt;p&gt;Growing one’s skill is an integral part of any craftsman’s career. As we touched before, thanks to the internet, it is not very easy to learn from books, videos and courses from great masters.&lt;/p&gt;
&lt;h3&gt;Books&lt;/h3&gt;
&lt;p&gt;Despite the newer technologies, books are still the most time and cost effective way to learn new skills. There are several books that is a must for every aspiring software craftsman to read:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672/ref=sr_1_1?s=books&amp;amp;ie=UTF8&amp;amp;qid=1467983893&amp;amp;sr=1-1&amp;amp;keywords=refactoring&quot;&gt;Refactoring&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And books that I have read and personally recommend:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://itunes.apple.com/au/book/swift-programming-language/id881256329?mt=11&quot;&gt;Swift&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Practice&lt;/h3&gt;
&lt;p&gt;The old saying “Practice makes perfect” is only half true. Not all kinds of practice are created equal. Studies have shown that &lt;a href=&quot;https://en.wikipedia.org/wiki/Practice_(learning_method)#Deliberate_practice&quot;&gt;Deliberate Practice&lt;/a&gt; is the superior method to practice.&lt;/p&gt;
&lt;p&gt;In other word, the practice has to be about improving specific skill, repeated, with feedback from expert and must be challenging. There is no point to keep practicing a skill that has already been mastered.&lt;/p&gt;
&lt;p&gt;One way to practice software skills is to do &lt;a href=&quot;http://www.codekatas.org/&quot;&gt;Code Kata&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This way of practicing comes from the martial arts world. When a Karate practitioner is alone, he can’t practice Kumite (sparring). To practice, he can do a Kata, which is a set pattern of movements involving stepping, punching, kicking and other moves.&lt;/p&gt;
&lt;p&gt;Code Kata is similar to that. You will solve a short programming exercise that can be done in multiple ways. Those katas are usually 15-60 minutes.&lt;/p&gt;
&lt;p&gt;Remember to always get feedback for your practice to minimize errors and avoid forming bad habits.&lt;/p&gt;
&lt;h3&gt;Share&lt;/h3&gt;
&lt;p&gt;Imagine that you have two groups of people. on the left you have team mango. on the right team avocado. they are about to attend a lecture.&lt;/p&gt;
&lt;p&gt;Team mango was told that they will have to teach the material to others. Team avocado was told that they will be tested on this.&lt;/p&gt;
&lt;p&gt;Which team do you think scores better on a test?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“When teachers prepare to teach, they tend to seek out key points and organize information into a coherent structure, our results suggest that students also turn to these types of effective learning strategies when they expect to teach.”&lt;/p&gt;
&lt;p&gt;~ John F. Nestojko&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Team mango performs significantly better than team avocado.&lt;/p&gt;
&lt;p&gt;This is based on a &lt;a href=&quot;http://psych.wustl.edu/memory/nestojko/NestojkoBuiKornellBjork%282014%29.pdf&quot;&gt;study in St Louis Washington University&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Even the intention to teach will improve your retention. So I want you to approach your learning from now on with different perspective.&lt;/p&gt;
&lt;p&gt;We teach to learn more effectively.&lt;/p&gt;
&lt;h3&gt;Blogging&lt;/h3&gt;
&lt;p&gt;I know most of us codes everyday and can’t really be bothered to write after we get home, But blogging/writing is one of the most cost effective way to share knowledge. It benefit you as you need to deconstruct what you have learned and structure it.&lt;/p&gt;
&lt;p&gt;Personally I have been blogging on and off for several years on different topics. Never consistently. I am planning to change that with the new revelation about the effectiveness of sharing and craftsmanship. There will be consistency.&lt;/p&gt;
&lt;p&gt;It helps to keep me accountable.&lt;/p&gt;
&lt;h3&gt;Brown Bag Sessions&lt;/h3&gt;
&lt;p&gt;This is where you gather your work colleagues and do knowledge sharing over take-away lunch (usually in brown paper bags). An intimate atmosphere where you know most people and conducted in familiar place. After you’ve done a couple of those, try to find a larger forum to share. For example, &lt;a href=&quot;http://www.meetup.com/MelbourneMobile/&quot;&gt;Meetup group&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Meetup Groups&lt;/h3&gt;
&lt;p&gt;The easiest way to share with more people is to find a meetup group in your niche and register. You can then ask the organizer if they are still looking for people to speak. They usually are. When it is confirmed, just prepare yourself and practice, then deliver it confidently.&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;Software development is more craft than engineering because it relies more on the skill and experience of the craftsman than the process of engineering.&lt;/p&gt;
&lt;p&gt;A good craftsman will always strive to grow themselves through continuous learning and sharing.&lt;/p&gt;
&lt;p&gt;Beside helping others, sharing will also help you because it forces you to learn effectively and understand the material more.&lt;/p&gt;
&lt;p&gt;Thanks for reading and I hope this essay benefit you.&lt;/p&gt;
</content:encoded></item><item><title>Streamlining API with Swagger.io</title><link>https://www.augusteo.com/blog/api-swagger-io/</link><guid isPermaLink="true">https://www.augusteo.com/blog/api-swagger-io/</guid><description>Step-by-step tutorial to run Sinatra server and Swift client</description><pubDate>Sat, 12 Mar 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This article is a companion of the talk I did for &lt;a href=&quot;http://www.meetup.com/MelbourneMobile/events/226525184/&quot;&gt;Melbourne Mobile&lt;/a&gt; about Swagger.
&lt;img src=&quot;https://www.augusteo.com/assets/blog/api-swagger-io/1_FiejaxmxNLxFe33vFpsu5g.jpg&quot; alt=&quot;1_FiejaxmxNLxFe33vFpsu5g.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You can find the slides at &lt;a href=&quot;http://www.slideshare.net/augusteo/streamlining-api-with-swaggerio&quot;&gt;SlideShare&lt;/a&gt;. I’m skipping majority of the contents covered in the slides, so please read through them first =)&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;What is Swagger?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;It is a group of projects under the &lt;a href=&quot;http://swagger.io/&quot;&gt;Swagger&lt;/a&gt; banner that works together to ease the pain of developing and documenting API. In this article’s scope, we are only going to play with Swagger-editor&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Installing Swagger-editor locally&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Just run these commands in your terminal of choice:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git clone https://github.com/swagger-api/swagger-editor.gitcd swagger-editornpm start&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The ‘npm start’ will take quite a bit of time to start because it has to install all the dependencies and compiles the app. When it is done, it will launch a web browser with your locally installed Swagger-editor.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://swagger.io/swagger-editor/&quot;&gt;read more about Swagger-editor…&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Swagger specs to write in the editor&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;When the editor launches, we can start writing our Swagger spec into it and see how it will be displayed on future Swagger-ui. If you are wondering about the syntax that Swagger can understand, please head over to the &lt;a href=&quot;https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md&quot;&gt;OpenAPI github page&lt;/a&gt;. That page can also be accessed from Swagger-editor; just click &lt;strong&gt;Help&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Swagger 2.0 Specs&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Play around with it for a while :)&lt;/p&gt;
&lt;p&gt;Now put this code-block into the editor panel:&lt;/p&gt;
&lt;p&gt;Our API specs for Hello World and sum two numbers&lt;/p&gt;
&lt;p&gt;The code should be pretty self-explanatory. The only thing that isn’t obvious would be the property &lt;strong&gt;operationId&lt;/strong&gt;, which means nothing in Swagger-UI now but will be used as method name in the code generated for server and client stub.&lt;/p&gt;
&lt;p&gt;Now the UI (right hand side) should display the API in an easy-to-understand format. The ‘Try this operation’ will fail now, because we do not have a server running on port 4567 to handle the request yet.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Generating server and client stub&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Now click on the ‘&lt;strong&gt;Generate Server&lt;/strong&gt;’ and choose ‘&lt;strong&gt;Sinatra&lt;/strong&gt;’. Wait for your web browser to download the zip file.&lt;/p&gt;
&lt;p&gt;After that, please do the same for ‘&lt;strong&gt;Generate Client&lt;/strong&gt;’ and ‘&lt;strong&gt;Swift&lt;/strong&gt;’.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Fleshing out Sinatra implementation&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;When you unzip the generated server stub file, you should be getting this folder structure:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.├── Gemfile├── Gemfile.lock├── README.md├── api│ └── default_api.rb├── config.ru├── lib│ └── swaggering.rb├── my_app.rb└── swagger.yaml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Feel free check out the files to see what’s in there. The ‘&lt;strong&gt;README.md&lt;/strong&gt;’ is especially helpful.&lt;/p&gt;
&lt;p&gt;The bulk of the generated API will be inside ‘&lt;strong&gt;api/default_api.rb&lt;/strong&gt;’. Please open that file using your favourite editor.&lt;/p&gt;
&lt;p&gt;Now you shall see the two methods inside the file for the two API that we had specified in the editor before. Feel free to read through it.&lt;/p&gt;
&lt;p&gt;Swagger-codegen will just generate the stubbed boilerplate for the api. It doesn’t know how to implement the functions to return correct result. If we just run the Sinatra server now, all we are getting would be:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;{“message” =&amp;gt; “yes, it worked”}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;We don’t want that, do we?&lt;/p&gt;
&lt;p&gt;Lets flesh out the implementation for the route &lt;strong&gt;/hello/{name}&lt;/strong&gt;. Please replace this code block:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;{“message” =&amp;gt; “yes, it worked”}.to_json&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;to:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;helloName = params[:name].to_s{“message” =&amp;gt; “Hello #{helloName}”}.to_json&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Sinatra will take the name parameter, parse it to string, and save it in variable helloName. It will then return the properly constructed Json message.&lt;/p&gt;
&lt;p&gt;Let’s do the same for the route &lt;strong&gt;/sum/{first}/{second}&lt;/strong&gt;. Replace the stubbed Json message with:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;firstNum = params[:first].to_isecondNum = params[:second].to_isum = firstNum + secondNum{“message” =&amp;gt; “The sum of #{firstNum} and #{secondNum} is #{sum}”}.to_json&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You can probably guess what this code does by now :)&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;default_api.rb&lt;/strong&gt; file should look like this:&lt;/p&gt;
&lt;p&gt;Yay, we are all done! See how easy it is to create a Sinatra server from scratch using the code generation?&lt;/p&gt;
&lt;p&gt;Lets run the server using command:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ rackup -p 4567 config.ru&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This command will spin up a &lt;a href=&quot;http://rack.github.io/&quot;&gt;Rack&lt;/a&gt; server at port 4567 running our api.&lt;/p&gt;
&lt;p&gt;After the server is running, you can go back to the Swagger-editor and play around with the ‘&lt;strong&gt;Try this operation&lt;/strong&gt;’.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Consume api on Swift app&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;This step is a bit convoluted for non iOS dev as it requires dependency management using Cocoapod. So we speed through most of it. You can skip ahead and get the ready-to-use codes in &lt;a href=&quot;https://github.com/augusteo/Swagger-Sinatra-Swift-Example/tree/master/swift-client&quot;&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The short story for iOS dev who wants to follow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;unzip the generated swift client from Swagger-editor&lt;/li&gt;
&lt;li&gt;create an empty iOS project in the same directory&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://iosdevtips.co/post/121756573323/ios-9-xcode-7-http-connect-server-error&quot;&gt;disable ATS&lt;/a&gt; to enable http call (iOS 9 forces https)&lt;/li&gt;
&lt;li&gt;close Xcode and cd into the project directory&lt;/li&gt;
&lt;li&gt;do &lt;strong&gt;pod init&lt;/strong&gt; to create an empty &lt;em&gt;Podfile&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;enter &lt;strong&gt;pod ‘SwaggerClient’, :path =&amp;gt; ‘../’&lt;/strong&gt; into the &lt;em&gt;Podfile&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;run &lt;strong&gt;pod install&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once you are done with pod install, please open up the generated xcworkspace. It should show a structure like this image&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.augusteo.com/assets/blog/api-swagger-io/1_o11XF3cwo96LhT182uDKYg.png&quot; alt=&quot;1_o11XF3cwo96LhT182uDKYg.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;project structure after pod install&lt;/p&gt;
&lt;p&gt;The generated codes will be inside &lt;strong&gt;DefaultAPI.Swift&lt;/strong&gt; file. You can see that it has two methods:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;public class func greetUser(name name: String) -&amp;gt; RequestBuilder&amp;lt;Inline_response_200&amp;gt; { ... }public class func sumTwo(first first: Int, second: Int) -&amp;gt; RequestBuilder&amp;lt;Inline_response_200&amp;gt; { ... }&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This is actually functions that we can use straight away in our iOS app. Lets get started!&lt;/p&gt;
&lt;p&gt;Open up the &lt;strong&gt;ViewController.Swift&lt;/strong&gt; and import SwaggerClient just below the import UIKit statement. The import should look like:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;import UIKitimport SwaggerClient&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Next we need to add a code to let SwaggerClient know our Sinatra server URL:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SwaggerClientAPI.basePath = “[http://localhost:4567](http://localhost:4567/)&quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;viewDidLoad()&lt;/strong&gt; method should now look like&lt;/p&gt;
&lt;p&gt;Now that Swagger know where our server is, just call the generated method!&lt;/p&gt;
&lt;p&gt;Press cmd+R to run the project. If everything is working like its supposed to, your Xcode console should look like:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Hello Melbourne&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Great! We have connected the Swift client to the server by just writing three lines of codes! Lets add the next api call just below the &lt;strong&gt;greetUser()&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The console should show:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Hello MelbourneThe sum of 2 and 4 is 6&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;All done!&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;During the course of this tutorial, we have learned how to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;install and run Swagger-editor locally&lt;/li&gt;
&lt;li&gt;write Swagger spec using OpenAPI specs&lt;/li&gt;
&lt;li&gt;generate Sinatra server and Swift client using Swagger-codegen&lt;/li&gt;
&lt;li&gt;fleshing out the Sinatra implementation&lt;/li&gt;
&lt;li&gt;consuming the api using generated Swift package&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Thanks for reading, I hope this is useful for you. Please let me know if you have questions :)&lt;/p&gt;
&lt;p&gt;The project is up on Github: &lt;a href=&quot;https://github.com/augusteo/Swagger-Sinatra-Swift-Example&quot;&gt;https://github.com/augusteo/Swagger-Sinatra-Swift-Example&lt;/a&gt;&lt;/p&gt;
</content:encoded></item></channel></rss>