<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Quinn Wang</title>
    <description>The latest articles on DEV Community by Quinn Wang (@codejs_1959).</description>
    <link>https://dev.to/codejs_1959</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4062959%2F04dfed64-921d-4a67-8963-f4e329c32a72.png</url>
      <title>DEV Community: Quinn Wang</title>
      <link>https://dev.to/codejs_1959</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codejs_1959"/>
    <language>en</language>
    <item>
      <title>The First 15 Minutes Are Usually One Step in Disguise</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Sun, 08 Nov 2026 19:40:46 +0000</pubDate>
      <link>https://dev.to/codejs_1959/the-first-15-minutes-are-usually-one-step-in-disguise-12k2</link>
      <guid>https://dev.to/codejs_1959/the-first-15-minutes-are-usually-one-step-in-disguise-12k2</guid>
      <description>&lt;p&gt;The step that decides whether a new tool survives your first fifteen minutes is rarely the tool itself; it is the one step you end up doing twice.&lt;/p&gt;

&lt;p&gt;I keep running into the same pattern when I time a first-run session: the total elapsed time tells you almost nothing useful, because a single clumsy step quietly pays for itself three or four times. You sign in, you get bounced, you sign in again, and now the clock has doubled while the step count barely moved. Wouldn't it be better to measure the rework instead of the wall clock, since rework is the part you can actually delete?&lt;/p&gt;

&lt;p&gt;So that is what this teardown does. I am going to walk through a small friction log that records each step, its duration, its exit code, and whether you had to redo it, and then I will show the one fix that mattered in the walkthrough I use below. The numbers in that walkthrough are a worked example you should replace with your own; please do not read them as a benchmark of any tool, including the one I mention later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why step counts lie to you
&lt;/h2&gt;

&lt;p&gt;A setup guide is a list of steps, and a list of steps invites you to count them, which is exactly the wrong unit. Counting steps rewards guides that merge five confusing actions into one opaque command, and it punishes guides that are honest about the five actions. The thing your patience actually reacts to is interruption: the moment you stop being a visitor and start being a repair technician.&lt;/p&gt;

&lt;p&gt;Rework has a nasty compounding shape too. A two-minute step you repeat three times costs more than a seven-minute step you do once, and it also costs you context, because each retry forces you to reload the mental state you had built. That is the analogy I keep coming back to: a single missing screw in a flat-pack shelf is not a small defect, because you disassemble the whole shelf to reach it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A friction log you can run in one minute
&lt;/h2&gt;

&lt;p&gt;The script below is deliberately tiny and dependency-free, and it wraps whatever entry point you already have rather than assuming a particular CLI. It writes tab-separated records, so you can paste the output straight into a spreadsheet or pipe it into the report that follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# friction.sh — rework-aware log for a first-run session.&lt;/span&gt;
&lt;span class="c"&gt;# Usage:  source ./friction.sh   then call step / rework.&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-uo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FRICTION_LOG&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;friction&lt;/span&gt;&lt;span class="p"&gt;.log&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# step &amp;lt;label&amp;gt; &amp;lt;command...&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;# Records wall time in ms, exit code, and an ISO timestamp.&lt;/span&gt;
step&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;shift
  local &lt;/span&gt;t0 t1 rc
  &lt;span class="nv"&gt;t0&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s%3N&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;rc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;
  &lt;span class="nv"&gt;t1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s%3N&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'STEP\t%s\t%s\t%s\t%s\n'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$label&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;t1 &lt;span class="o"&gt;-&lt;/span&gt; t0&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$rc&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; +%FT%TZ&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$rc&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# rework &amp;lt;label&amp;gt; &amp;lt;ms_lost&amp;gt;  — call this the moment you realise you must redo a step.&lt;/span&gt;
rework&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'REWORK\t%s\t%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that &lt;code&gt;rework&lt;/code&gt; takes a number you supply yourself, and that is intentional. You cannot measure the time you spent confused with a timestamp, because the confusion often starts before the command does, but you can absolutely write down your own estimate at the moment you notice it. An honest guess beats a fake precision every time.&lt;/p&gt;

&lt;p&gt;Here is a session shape, using placeholders you should replace with your own entry point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ./friction.sh

step &lt;span class="s2"&gt;"read-landing-page"&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;          &lt;span class="c"&gt;# always false-start: reading is a step&lt;/span&gt;
step &lt;span class="s2"&gt;"create-account"&lt;/span&gt;    &lt;span class="nb"&gt;true&lt;/span&gt;          &lt;span class="c"&gt;# substitute your real command&lt;/span&gt;
step &lt;span class="s2"&gt;"first-answer"&lt;/span&gt;      &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENTRY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt;

&lt;span class="c"&gt;# the machine asked for a second sign-in before answering:&lt;/span&gt;
rework &lt;span class="s2"&gt;"first-answer"&lt;/span&gt; 90000            &lt;span class="c"&gt;# 90s of repeated work, your estimate&lt;/span&gt;

step &lt;span class="s2"&gt;"second-answer"&lt;/span&gt;     &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENTRY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The report is three lines of &lt;code&gt;awk&lt;/code&gt; and gives you the only two numbers worth keeping: how many steps failed loudly, and what share of the session was rework.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'\t'&lt;/span&gt; &lt;span class="s1"&gt;'
  $1=="REWORK" { rework += $3; next }
  $1=="STEP"   { total += $3; n++; if ($4 != 0) failed++ }
  END {
    denied = total + rework
    printf "steps=%d failed=%d work=%.1fs rework=%.1fs\n", n, failed, total/1000, rework/1000
    if (denied &amp;gt; 0) printf "rework share=%.0f%%\n", 100*rework/denied
  }'&lt;/span&gt; friction.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What the example session showed me
&lt;/h2&gt;

&lt;p&gt;In my worked example, four steps took roughly two minutes of real work, and one of them — getting a first answer out of the tool — was retried once for ninety seconds. That single retry was not the longest step, but it owned most of the session's cost, and it also produced the only moment where I considered closing the tab. Ask yourself which is worse for a first impression: a slow step that works, or a fast step that makes you start over?&lt;/p&gt;

&lt;p&gt;That is why I now ignore the total and sort by rework share first. A step with high rework is a design bug wearing a progress bar, while a step that is merely slow is usually just the price of doing something real. Sorting this way also keeps me honest about my own impatience, because it distinguishes "this is slow" from "this made me do it again" and only the second one is the tool's fault.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one fix that mattered
&lt;/h2&gt;

&lt;p&gt;The fix was not a setting, a flag, or a better error message; it was reordering the guide so the shortest path to a first successful answer comes before any local installation. In practice that means you prove the round trip on someone else's machine first, and only then earn the right to configure your own. Setting up a local environment before you know the tool can answer you is like installing a kitchen before checking the water is on.&lt;/p&gt;

&lt;p&gt;This is where hosted options genuinely change the shape of the first fifteen minutes. MonkeyCode is an open-source AI coding project, and its operator-supplied offer at the time of writing (November 2026) includes free model access and a free server option, advertised alongside a 10-million-token allotment; treat all of that as a time-sensitive claim to verify on its own pages rather than a permanent guarantee. Disclosure: This article was prepared as part of MonkeyCode's product outreach. What matters for this teardown is narrower than the marketing: a hosted first answer removes the install step from the rework-prone part of the session, which is the exact defect the log is built to surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing a first path, honestly
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Start hosted or free server&lt;/th&gt;
&lt;th&gt;Start local install&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Evaluating whether the tool fits your problem&lt;/td&gt;
&lt;td&gt;Yes — first answer before config&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network policy blocks hosted inference&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes, with a pinned version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reproducing a bug across machines&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes — local only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Teaching a workshop in 30 minutes&lt;/td&gt;
&lt;td&gt;Yes — removes account roulette&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Handling regulated source code&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes, only if you have cleared it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read the table as a default, not a rule, and re-run your own log whenever the guide changes, because onboarding decays faster than documentation admits. The log takes a minute, and it will tell you more about a tool's DX than any feature list ever could.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations, and who should skip this
&lt;/h2&gt;

&lt;p&gt;This method measures your session, not the product, and a single run has no statistical weight, so collect three or four sessions before you draw a conclusion. It also cannot see the cost you pay after the first fifteen minutes, which is where most real friction lives, and it says nothing about output quality.&lt;/p&gt;

&lt;p&gt;If your team has data-residency constraints, needs a pinned model version for reproducible builds, or requires an SLA for production traffic, a free hosted path is the wrong default and you should start local. If you already have a working setup, this exercise is mostly a way to feel productive without shipping anything, so skip it. And if you cannot verify the current offer terms yourself, do not plan capacity around them.&lt;/p&gt;

&lt;p&gt;If you want a disposable place to try the log, the free server path is the cheapest first answer you can get; go break it deliberately, then tell me which step made you start over.&lt;/p&gt;

</description>
      <category>dx</category>
      <category>ai</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Your First Run Has a Tab Budget</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Sat, 07 Nov 2026 18:23:54 +0000</pubDate>
      <link>https://dev.to/codejs_1959/your-first-run-has-a-tab-budget-375o</link>
      <guid>https://dev.to/codejs_1959/your-first-run-has-a-tab-budget-375o</guid>
      <description>&lt;p&gt;The first fifteen minutes of a new project almost never fail because some step is slow. They fail because you keep leaving the editor — one tab for the install docs, one for the auth page, one for the cloud console, one for the ticket where someone promised the env var already exists. If you have ever finished a setup session feeling tired without being able to name a single slow command, you already know this feeling; the wall clock was fine, but your attention was chopped into pieces. So the metric I want you to instrument is not minutes per step, it is &lt;strong&gt;how many times you leave the editor&lt;/strong&gt;, and the fix that matters is almost always the same: collapse the browser-shaped steps into one command you can run without thinking.&lt;/p&gt;

&lt;p&gt;Why does timing mislead us here? A &lt;code&gt;npm ci&lt;/code&gt; that takes ninety seconds is annoying, but it is also a single uninterrupted block you can spend reading the diff of the repo you just cloned. Five separate thirty-second detours to find out which Node version the CI uses are worse, because each one costs you the reload of the mental model you had built up. Duration is additive and predictable; interruption is multiplicative and invisible. That is why a stopwatch on your setup script tells you almost nothing useful, and why the same project can feel smooth to one developer and hostile to another who simply had more tabs open.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the ledger before you build the fix
&lt;/h2&gt;

&lt;p&gt;The artifact here is tiny: a script that timestamps every time you step away, so the shape of the problem shows up as a list rather than a vague memory. It is not clever and it is not automated — you type one command when you leave, one command when you come back, and that manual friction is the point, because it forces you to notice the switch while it is happening.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# tablog — record every time you leave the editor during first-run setup&lt;/span&gt;
&lt;span class="c"&gt;# usage: ./tablog start "clone repo"   then   ./tablog mark "docs: node version"&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail
&lt;span class="nv"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TABLOG_FILE&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="p"&gt;/.tablog.csv&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;unlabeled&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;epoch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;stamp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; +%Y-%m-%dT%H:%M:%SZ&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
  &lt;/span&gt;start&lt;span class="p"&gt;)&lt;/span&gt;  : &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s,%s,%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$epoch&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$stamp&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$label&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
  mark&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s,%s,%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$epoch&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$stamp&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$label&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
  report&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR==1{start=$1; prev=$1; next}
                   {printf "%5ds  %s\n", $1-prev, $3; prev=$1}
                   END{printf "%5ds  total elapsed\n", prev-start}'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
  buckets&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR&amp;gt;1{split($3,p," "); b[p[1]]++} END{for (k in b) print b[k], k}'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"usage: &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; {start|mark|report|buckets} [label]"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;64 &lt;span class="p"&gt;;;&lt;/span&gt;
hesac
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;./tablog start "fresh clone"&lt;/code&gt; in a clean checkout of a project you have never touched, then &lt;code&gt;./tablog mark "..."&lt;/code&gt; each time you switch away and each time you come back. The &lt;code&gt;report&lt;/code&gt; mode prints one line per gap so you can see the rhythm; the &lt;code&gt;buckets&lt;/code&gt; mode groups labels by their first word, which is how you discover whether your interruptions are mostly &lt;code&gt;docs&lt;/code&gt;, mostly &lt;code&gt;auth&lt;/code&gt;, or mostly &lt;code&gt;cloud&lt;/code&gt;. An illustrative session, not a real one — yours will differ — might read &lt;code&gt;docs 6&lt;/code&gt;, &lt;code&gt;auth 3&lt;/code&gt;, &lt;code&gt;cloud 4&lt;/code&gt;, and that distribution is the whole diagnosis. Six trips to the docs is not a documentation problem; it is a repository problem, because the answers should have been in the repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one fix: move the browser steps into the repo
&lt;/h2&gt;

&lt;p&gt;Here is the change that actually moved the needle for the pattern above, and it is embarrassingly small. Whatever you had to look up in a browser during the first run goes into a single checked-in file, and every step it describes becomes a target in one entry point, so the answer to "how do I start this?" is a command rather than a search.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;first-run&lt;/span&gt;
&lt;span class="nl"&gt;first-run&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env-check deps migrate smoke&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"first run complete — you never left this terminal"&lt;/span&gt;

&lt;span class="nl"&gt;env-check&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; .env &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$$&lt;/span&gt;&lt;span class="s2"&gt;DATABASE_URL"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"DATABASE_URL is empty; see .first-run/README.md"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nl"&gt;deps&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    npm ci &lt;span class="nt"&gt;--no-audit&lt;/span&gt; &lt;span class="nt"&gt;--no-fund&lt;/span&gt;

&lt;span class="nl"&gt;migrate&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    npm run migrate

&lt;span class="nl"&gt;smoke&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    npm run &lt;span class="nb"&gt;test&lt;/span&gt;:smoke
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what this does not do: it does not make anything faster. &lt;code&gt;npm ci&lt;/code&gt; still takes as long as it takes, and the migration still has to run. What it removes is the decision layer — the moment where you wonder whether you should run migrations before or after seeding, and open a tab to check. Three or four of those decisions are normal in a new repo; ten of them is a tax you are paying for someone else's undocumented assumptions. The &lt;code&gt;.first-run/README.md&lt;/code&gt; next to the Makefile is where the pinned versions, the required env vars, and the "yes, the smoke test fails the first time, run it twice" note live, so the repo answers before the browser does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where a hosted dev environment fits
&lt;/h2&gt;

&lt;p&gt;One class of interruption is different from the rest: the toolchain itself. Installing a language runtime, a database, and a compiler on a fresh laptop is where the tab count explodes, and it is also the part with the least to do with your actual code. This is the point at which a hosted option is worth evaluating — I have been looking at MonkeyCode, which the operator describes as an open-source project offering free model access and a free server option, and the operator currently states that the free allowance is 10 million tokens. Verify the current numbers and terms on the project page before you plan around them, because allowances and server terms in this space change on a schedule nobody announces.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: This article was prepared as part of MonkeyCode's product outreach.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The reason it belongs in a tab-count article rather than a model-quality article is narrow and honest: two of the biggest interruption sources in a first run are "install and configure a runtime" and "go get an API key and add a payment method". A hosted server removes the first from your laptop, and free model access removes the second from your afternoon. Neither makes your code better, and neither is a reason to move a regulated workload onto someone else's machine.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Local toolchain&lt;/th&gt;
&lt;th&gt;Hosted free server&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Trying a repo for the first time&lt;/td&gt;
&lt;td&gt;Fine, but high tab count&lt;/td&gt;
&lt;td&gt;Strong fit: one fewer install&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data you cannot ship off-machine&lt;/td&gt;
&lt;td&gt;Required&lt;/td&gt;
&lt;td&gt;Do not use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pinned, reproducible CI image&lt;/td&gt;
&lt;td&gt;Required&lt;/td&gt;
&lt;td&gt;Not a substitute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Offline or flaky-network work&lt;/td&gt;
&lt;td&gt;Required&lt;/td&gt;
&lt;td&gt;Poor fit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prototype you will delete Friday&lt;/td&gt;
&lt;td&gt;Overkill&lt;/td&gt;
&lt;td&gt;Strong fit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What this method cannot tell you
&lt;/h2&gt;

&lt;p&gt;The ledger is manual, so it measures what you remember to log, and a developer who forgets to log is exactly the developer having the worst time. It also says nothing about severity: a single twenty-minute detour to fix a broken certificate is worse than eight quick doc checks, and the count alone will not show you that. And if your first run is already a single &lt;code&gt;make first-run&lt;/code&gt; that takes four minutes, stop reading — instrumenting that is pure overhead, and the honest answer is that someone already did this work for you.&lt;/p&gt;

&lt;p&gt;So try it on the next repo you clone. Start the log, work for fifteen minutes without pretending, and look at the labels instead of the durations. If the biggest bucket is &lt;code&gt;docs&lt;/code&gt;, your fix is a file in the repo; if it is &lt;code&gt;auth&lt;/code&gt; or &lt;code&gt;cloud&lt;/code&gt;, your fix might be a hosted environment; if it is &lt;code&gt;install&lt;/code&gt;, your fix is a container or a server, not more patience. Either way, the tab count tells you which one to fix first, and that beats guessing.&lt;/p&gt;

</description>
      <category>dx</category>
      <category>devtools</category>
      <category>productivity</category>
      <category>ai</category>
    </item>
    <item>
      <title>Clock the Blocked Minutes, Not the Blocked Steps</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Fri, 06 Nov 2026 17:02:40 +0000</pubDate>
      <link>https://dev.to/codejs_1959/clock-the-blocked-minutes-not-the-blocked-steps-5g7a</link>
      <guid>https://dev.to/codejs_1959/clock-the-blocked-minutes-not-the-blocked-steps-5g7a</guid>
      <description>&lt;p&gt;The number that predicts whether a newcomer ships in week one is not install time; it is the minutes they spend blocked on another human. I have watched teams cut a twelve-step setup down to four and still lose the new developer, because every remaining step ended with the same sentence: ask someone for access. So before you tune another install script, answer a less comfortable question — do you actually know which of those first fifteen minutes were spent waiting on a machine, and which were spent waiting on a person?&lt;/p&gt;

&lt;p&gt;That split matters more this year than last, because most of the noise around AI coding tools is about machine speed. Delete ten seconds of inference latency and nobody notices; delete a forty-minute permissions handoff and the entire first day changes shape. Which is why I now log onboarding friction as two separate clocks, and why the harness below is the only setup tool I keep re-using across projects instead of rewriting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two clocks, one TSV row
&lt;/h2&gt;

&lt;p&gt;The first clock is machine wait: package installs, cold starts, image pulls, queue time, token generation. The second is human wait: the Slack message asking for a repository invite, the review of your service account request, the "hey, does anyone know how staging is seeded?" thread. Machine waits feel expensive because they are visible in a terminal, but they are short, repeatable, and usually fixable by caching; human waits are invisible, unbounded, and they are the part people actually remember about their first day.&lt;/p&gt;

&lt;p&gt;So the artifact here is deliberately boring. It is a tiny shell library that wraps a command, records a start and end timestamp in milliseconds, and tags the block as &lt;code&gt;human&lt;/code&gt; or &lt;code&gt;machine&lt;/code&gt;. You run it around each phase of a first-run session, you get a TSV file, and you get ratios instead of anecdotes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# frictionlog.sh — two clocks for a first-run session: machine wait, human wait.&lt;/span&gt;
&lt;span class="c"&gt;# needs: bash, python3 (for portable millisecond timestamps)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-uo&lt;/span&gt; pipefail
&lt;span class="nv"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FRICTION_LOG&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;friction&lt;/span&gt;&lt;span class="p"&gt;.tsv&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'phase\twaiting_on\tstart_ms\tend_ms\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

now_ms&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'import time; print(int(time.time() * 1000))'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

block&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;            &lt;span class="c"&gt;# block &amp;lt;human|machine&amp;gt; &amp;lt;phase&amp;gt; -- &amp;lt;cmd...&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;waiter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;phase&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;shift &lt;/span&gt;2
  &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"--"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;shift
  local &lt;/span&gt;start&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;now_ms&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"$#"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\t%s\t%s\t%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$phase&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$waiter&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$start&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;now_ms&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

begin&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;__waiter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;__phase&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;__start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;now_ms&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;   &lt;span class="c"&gt;# open a wait with no command&lt;/span&gt;
end&lt;span class="o"&gt;()&lt;/span&gt;   &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\t%s\t%s\t%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$__phase&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$__waiter&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$__start&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;now_ms&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;begin&lt;/code&gt;/&lt;code&gt;end&lt;/code&gt; pair exists because the worst waits have no command to wrap — the command is &lt;em&gt;wait for a reply&lt;/em&gt;. You call &lt;code&gt;begin human "secrets: ask platform team"&lt;/code&gt; when you send the message and &lt;code&gt;end&lt;/code&gt; when access lands; one open human wait at a time is realistic enough for onboarding. A session then looks like this, and yes, it is meant to look unglamorous:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ./frictionlog.sh
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;FRICTION_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;run1.tsv

block machine &lt;span class="s2"&gt;"clone"&lt;/span&gt;         &lt;span class="nt"&gt;--&lt;/span&gt; git clone &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 git@github.com:acme/webapp.git
block machine &lt;span class="s2"&gt;"deps/install"&lt;/span&gt;  &lt;span class="nt"&gt;--&lt;/span&gt; npm ci &lt;span class="nt"&gt;--no-audit&lt;/span&gt; &lt;span class="nt"&gt;--no-fund&lt;/span&gt;
begin human   &lt;span class="s2"&gt;"secrets: ask platform team"&lt;/span&gt;      &lt;span class="c"&gt;# send the message&lt;/span&gt;
&lt;span class="c"&gt;# ... coffee, hopefully ...&lt;/span&gt;
end
block machine &lt;span class="s2"&gt;"first build"&lt;/span&gt;   &lt;span class="nt"&gt;--&lt;/span&gt; npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reading the file instead of arguing about it
&lt;/h2&gt;

&lt;p&gt;A TSV that nobody reduces is just a diary, so here is the whole analysis step in awk — no dashboard, no vendor, no new dependency. It prints total seconds per phase and the share of that phase attributed to a human:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight awk"&gt;&lt;code&gt;&lt;span class="nx"&gt;awk&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;F&lt;/span&gt;&lt;span class="err"&gt;'\&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="kc"&gt;NR&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$4&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
  &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;
  &lt;span class="nx"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"human"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;human&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kr"&gt;END&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%-30s %8.1fs   human-blocked %5.1f%%\n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;human&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%-30s %8.1fs\n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"TOTAL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;run1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tsv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is illustrative output showing the &lt;em&gt;shape&lt;/em&gt; of a result, not a benchmark of any tool or machine: &lt;code&gt;secrets: ask platform team&lt;/code&gt; at roughly 2410s and 100.0% human-blocked, &lt;code&gt;deps/install&lt;/code&gt; at 74.2s, &lt;code&gt;first build&lt;/code&gt; at 38.9s, &lt;code&gt;first test&lt;/code&gt; at 11.6s. Look at that ratio for a second — was your last onboarding fix aimed at the 74 seconds or at the 40 minutes? The reducer answers that in one line, and it is very hard to argue with a number you produced yourself in your own repository.&lt;/p&gt;

&lt;p&gt;One discipline point I keep relearning: measure the &lt;em&gt;second&lt;/em&gt; run, not the first. The first run is dominated by one-time state and flatters whatever you already optimized, while the second run shows what a returning developer pays every single day. Keep both files, compare them, and be honest about which number your team is actually paid to care about.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one fix that mattered
&lt;/h2&gt;

&lt;p&gt;In the session above, the fix was not caching and it was not a new devcontainer. The fix was replacing "ask a person for credentials" with "start in a throwaway environment that needs no credentials to build" — a disposable box plus a read-only fixture beats a faster package manager, because it converts a human wait into a machine wait you can queue and forget.&lt;/p&gt;

&lt;p&gt;That is the honest place where MonkeyCode's free model access and free server option enter this workflow, and it is a narrower claim than a launch post would make. For the eval-shaped part of onboarding — read this file, write a test, explain this stack trace — a free sandbox with a free token allowance removes two waits that normally stall the first hour: waiting for budget approval, and waiting for someone to provision a machine. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The team advertises a free allowance on the order of ten million tokens and a free server option at the time of writing; that number is exactly the kind of thing that changes, so read the project page before you size a plan around it, and do not treat my prose or this post's date as the source of truth.&lt;/p&gt;

&lt;p&gt;I would also keep the open-source claim practical rather than romantic. Being able to read the implementation is genuinely useful when you need to know what leaves your machine, but it is not a security review, and it is not an SLA.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who should not use this
&lt;/h2&gt;

&lt;p&gt;If your repository needs production credentials to build, a free disposable environment is not your answer and no amount of free tokens changes that — decouple the build first. If you handle regulated data, run latency-sensitive CI, or need uptime guarantees for a demo, a free tier is the wrong layer and a boring paid box is the correct choice. And if your measured human-blocked share is above forty percent, stop shopping for tools entirely, because your bottleneck is a permission model and every new AI feature is a distraction from it.&lt;/p&gt;

&lt;p&gt;Which brings me back to this week's loudest argument about AI and jobs. The interesting question is not whether a model can do the work; it is which waits your team has quietly normalized for years and stopped seeing. Clock both kinds, publish the ratio where the team can read it, and let the numbers pick the fix — would you rather save ten seconds of inference, or one expensive afternoon of asking?&lt;/p&gt;

&lt;p&gt;If you want to run the harness against something disposable, the free tier is a fair place to try it; bring the TSV back with you, not just the vibes.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devtools</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Budget the Undo Before You Budget the Install</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Thu, 05 Nov 2026 15:38:09 +0000</pubDate>
      <link>https://dev.to/codejs_1959/budget-the-undo-before-you-budget-the-install-477h</link>
      <guid>https://dev.to/codejs_1959/budget-the-undo-before-you-budget-the-install-477h</guid>
      <description>&lt;p&gt;The first fifteen minutes of onboarding are not an installation problem at all; they are a reversibility problem wearing an installation costume. Everyone times the download, the package install, the first &lt;code&gt;npm run dev&lt;/code&gt;, and almost nobody times the undo. But when a new contributor gets stuck at minute twelve, the question they are actually asking is not "how do I go forward?" — it is "how do I get back to the state I trusted?" If you cannot answer that question quickly, every subsequent step feels expensive, and the onboarding budget quietly evaporates.&lt;/p&gt;

&lt;p&gt;So I stopped optimizing the install and started pricing the undo. For each step in a setup guide, I ask two questions in order: how many commands does it take to reverse, and have I ever run that reversal myself? Steps that pass both questions are cheap to try, and cheap-to-try steps get attempted instead of postponed. Steps that fail either question are the real blockers, even when they look like one-liners.&lt;/p&gt;

&lt;p&gt;What follows is a rehearsal workflow, not a benchmark. I am not going to quote timings I did not measure on your machine, because your network, your shell profile, and your company proxy will rewrite all of them anyway. Instead, here is a small artifact you can run today and a decision table that tells you where your fifteen minutes actually go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Snapshot first, install second
&lt;/h2&gt;

&lt;p&gt;The core move is embarrassingly simple: before the first mutating command, record what the machine looked like. I mean the cheap, boring facts — the commit you are on, whether the tree is dirty, which binaries exist and where they live, and which environment variables the setup guide is about to touch. The template below is the shape of the script I would start any repo with; adapt the binary list to your stack and treat it as a proposal you have to test, not as a finished tool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# rehearse.sh — run this BEFORE the first onboarding step.&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;STAMP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; +%Y%m%dT%H%M%SZ&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;LEDGER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;".onboard/&lt;/span&gt;&lt;span class="nv"&gt;$STAMP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LEDGER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

git rev-parse &lt;span class="nt"&gt;--is-inside-work-tree&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null
git rev-parse HEAD        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LEDGER&lt;/span&gt;&lt;span class="s2"&gt;/head.txt"&lt;/span&gt;
git status &lt;span class="nt"&gt;--porcelain&lt;/span&gt;    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LEDGER&lt;/span&gt;&lt;span class="s2"&gt;/dirty.txt"&lt;/span&gt;

&lt;span class="c"&gt;# Which interpreter/runner is this shell actually going to pick up?&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;bin &lt;span class="k"&gt;in &lt;/span&gt;node npm python3 uv docker make&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$bin&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\t%s\t%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$bin&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$bin&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$bin&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--version&lt;/span&gt; 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\tMISSING\t-\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$bin&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi
done&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LEDGER&lt;/span&gt;&lt;span class="s2"&gt;/bins.tsv"&lt;/span&gt;

&lt;span class="c"&gt;# Only the variables a setup guide realistically mutates.&lt;/span&gt;
&lt;span class="nb"&gt;env&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'^(PATH|NODE_OPTIONS|PYTHONPATH|HTTP_PROXY|HTTPS_PROXY|NO_PROXY)='&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LEDGER&lt;/span&gt;&lt;span class="s2"&gt;/env.txt"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true

echo&lt;/span&gt; &lt;span class="s2"&gt;"ledger written to &lt;/span&gt;&lt;span class="nv"&gt;$LEDGER&lt;/span&gt;&lt;span class="s2"&gt; — now you may install things"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point of &lt;code&gt;command -v&lt;/code&gt; plus &lt;code&gt;--version&lt;/code&gt; on the same line is that a version manager can lie to you. If &lt;code&gt;node&lt;/code&gt; resolves to a shim in &lt;code&gt;~/.nvm/...&lt;/code&gt; on your laptop and to &lt;code&gt;/usr/local/bin/node&lt;/code&gt; in CI, your setup guide has two audiences and only one of them is documented. Capturing the resolved path makes that divergence visible in the ledger instead of in a stack trace.&lt;/p&gt;

&lt;h2&gt;
  
  
  The undo has to be one command, and you have to run it
&lt;/h2&gt;

&lt;p&gt;A snapshot is worthless if restoring it requires judgment. The second script is deliberately blunt: reset to the recorded commit, show what untracked files would be removed, then stop and let a human look at the list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# undo.sh — usage: ./undo.sh .onboard/&amp;lt;stamp&amp;gt;&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail
&lt;span class="nv"&gt;LEDGER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;:?usage:&lt;span class="p"&gt; undo.sh .onboard/&amp;lt;stamp&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

git reset &lt;span class="nt"&gt;--hard&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LEDGER&lt;/span&gt;&lt;span class="s2"&gt;/head.txt"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
git clean &lt;span class="nt"&gt;-nd&lt;/span&gt;   &lt;span class="c"&gt;# -n is intentional: print the deletion list, delete nothing.&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"review the list above, then rerun with 'git clean -fd' if it is what you expect"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;-n&lt;/code&gt; flag is the entire lesson. A destructive command you have rehearsed is a tool; the same command unrehearsed is a threat, and threats make people hesitate at exactly the moment they should be experimenting. Running the dry-run at minute three also proves the ledger exists and is readable, which is a much better failure than discovering at minute forty that the snapshot directory was gitignored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the fifteen minutes actually go
&lt;/h2&gt;

&lt;p&gt;Once you have the two scripts, classifying steps becomes mechanical. The table below is the one I scribble next to a setup guide, and it has changed my mind about which steps to automate first.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step type&lt;/th&gt;
&lt;th&gt;Undo cost&lt;/th&gt;
&lt;th&gt;Rehearse it?&lt;/th&gt;
&lt;th&gt;What it really costs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Edit files inside the repo&lt;/td&gt;
&lt;td&gt;One &lt;code&gt;git reset --hard&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Automatic&lt;/td&gt;
&lt;td&gt;Almost nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create untracked files&lt;/td&gt;
&lt;td&gt;One reviewed &lt;code&gt;git clean&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Yes, with &lt;code&gt;-n&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;A minute of attention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Global package install&lt;/td&gt;
&lt;td&gt;Manual, often unknown&lt;/td&gt;
&lt;td&gt;Yes, on a disposable box&lt;/td&gt;
&lt;td&gt;Trust, not time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shell profile edits&lt;/td&gt;
&lt;td&gt;Manual, easy to forget&lt;/td&gt;
&lt;td&gt;Yes, diff the profile&lt;/td&gt;
&lt;td&gt;Future-you's debugging hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pulling container images&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker image rm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only if disk matters&lt;/td&gt;
&lt;td&gt;Bandwidth&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern is uncomfortable: the steps that take the longest to undo are usually the fastest to run. &lt;code&gt;npm install -g something&lt;/code&gt; finishes in seconds and can leave a shim in your &lt;code&gt;PATH&lt;/code&gt; that outlives the project. That asymmetry — instant forward, murky backward — is what makes a confident engineer go quiet and start reading the setup guide twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one fix that mattered
&lt;/h2&gt;

&lt;p&gt;If I had to ship exactly one change to a setup guide, it would not be a faster install or a better error message. It would be a refusal to proceed: a &lt;code&gt;make rehearse&lt;/code&gt; target that fails loudly when &lt;code&gt;.onboard/&lt;/code&gt; is missing, wired in front of the first mutating step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;rehearse undo&lt;/span&gt;
&lt;span class="nl"&gt;rehearse&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; .onboard &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ledger exists"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"run ./rehearse.sh first"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nl"&gt;undo&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-1d&lt;/span&gt; .onboard/&lt;span class="k"&gt;*&lt;/span&gt;/ | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt; | xargs &lt;span class="nt"&gt;-r&lt;/span&gt; ./undo.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why a gate instead of a paragraph of documentation? Because guidance is advisory and gates are structural. A contributor who skips the snapshot now gets a one-line error at the moment they can still cheaply comply, rather than a mystery at the end of the day. The gate also encodes the ordering knowledge that used to live in a senior engineer's head, which is the only durable way to transfer it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rehearse on a machine you are allowed to break
&lt;/h2&gt;

&lt;p&gt;There is one obvious problem with a drill: you need somewhere to run it that is not your only laptop. This is the one place where a free server option genuinely earns its keep, because a disposable machine turns a scary rehearsal into a boring one — snapshot, install, undo, destroy, repeat. The same reasoning applies to the "what does this error mean" step at minute nine, which is where free model access helps: a contributor can ask a model for a hypothesis about a failing &lt;code&gt;command -v&lt;/code&gt; result without waiting on a credit card, a seat license, or a procurement ticket. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The claim worth testing is narrow and testable — you get model access and a throwaway server at no cost, so use them for the undo drill, not for the happy path you already trust.&lt;/p&gt;

&lt;p&gt;Keep the loop honest, though. A rehearsal proves your script runs and that the rollback returns you to a known commit; it does not prove your production config is safe, and it says nothing about steps that live outside the repository. Anything installed at the system level, anything written into a shell profile, and anything pulled into a shared Docker daemon is only partially covered by a git-based ledger, and you should write that limitation into the guide instead of hoping nobody notices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who should skip this
&lt;/h2&gt;

&lt;p&gt;If your setup is genuinely one command against a container that already contains everything, the ledger is ceremony and you should drop it. The same is true on a monorepo so large that &lt;code&gt;git status&lt;/code&gt; itself costs more than the install, where a tarball snapshot of the relevant subdirectory will serve you better. And if your team has no review culture for destructive commands, adding &lt;code&gt;git reset --hard&lt;/code&gt; to a newcomer's toolbox without the &lt;code&gt;-n&lt;/code&gt; habit is a net negative — teach the dry run first, or teach neither.&lt;/p&gt;

&lt;p&gt;For everyone else, the experiment is cheap: spend the first three minutes of your next onboarding writing the ledger, spend one minute proving you can undo, and only then start installing. If the install still stalls, you will at least know that the person stuck can get back to safety without asking anyone — and isn't that the thing "developer experience" was supposed to mean in the first place?&lt;/p&gt;

</description>
      <category>dx</category>
      <category>devtools</category>
      <category>opensource</category>
      <category>ai</category>
    </item>
    <item>
      <title>Which Setup Step Actually Blocks Your First Run?</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Tue, 27 Oct 2026 13:12:25 +0000</pubDate>
      <link>https://dev.to/codejs_1959/which-setup-step-actually-blocks-your-first-run-56da</link>
      <guid>https://dev.to/codejs_1959/which-setup-step-actually-blocks-your-first-run-56da</guid>
      <description>&lt;p&gt;The step that decides whether a new contributor finishes setup is rarely the step your README spends the most words on. I learned this the boring way: I timed every step of a first run, wrote one JSON line per step, and only then saw which step was really eating the first fifteen minutes. The fix that mattered was not better prose for that step, it was a two-second version check that failed loudly before the expensive work started. What follows is the harness I use now, plus the two habits that stopped me from guessing when a run failed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aggregate setup time is a polite fiction
&lt;/h3&gt;

&lt;p&gt;When someone tells you setup takes ten minutes, ask them what they actually measured. They usually measured their own last successful run, on a warm cache, on the machine they have been using for a year. That number is not a distribution, it is a memory, and it says nothing about the person whose first run ends in a stack trace about engines. Would you rather fix a step that is slow, or a step that quietly decides whether people finish at all?&lt;/p&gt;

&lt;p&gt;Averages hide the shape of the problem, which is why I stopped reporting totals. A step that passes for eight people in two minutes and fails for two people in forty is not a two-minute step; it is a fork in the road with the sign pointing the wrong way. What I want to know is narrow and answerable: which step is both slow and unreliable, because that is the only one worth changing this week.&lt;/p&gt;

&lt;h3&gt;
  
  
  A preflight timer that fits in one file
&lt;/h3&gt;

&lt;p&gt;The harness is deliberately dull: bash, one JSONL file per run, no dashboard, no SDK, no telemetry I have to defend in a review. Each step records its wall time and a pass or fail, and the script keeps going after a failure, because the failure you did not record is the one that ruins your afternoon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# preflight.sh — time each setup step, record pass/fail as JSONL.&lt;/span&gt;
&lt;span class="c"&gt;# Unexecuted template: replace the step list with what your repo actually needs.&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-uo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;OUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;/tmp/preflight-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; +%Y%m%dT%H%M%SZ&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.jsonl&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUT&lt;/span&gt;&lt;span class="p"&gt;%.jsonl&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.log"&lt;/span&gt;
: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; : &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

now_ms&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'import time; print(int(time.time()*1000))'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

run_step&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;shift
  local &lt;/span&gt;start end status
  &lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;now_ms&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pass&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;else &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;fail&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nv"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;now_ms&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'{"step":"%s","ms":%d,"status":"%s"}\n'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;end &lt;span class="o"&gt;-&lt;/span&gt; start&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

run_step versions bash &lt;span class="nt"&gt;-lc&lt;/span&gt; &lt;span class="s1"&gt;'node --version &amp;amp;&amp;amp; python3 --version'&lt;/span&gt;
run_step clone    bash &lt;span class="nt"&gt;-lc&lt;/span&gt; &lt;span class="s1"&gt;'git clone --depth 1 "$REPO_URL" /tmp/pf-repo'&lt;/span&gt;
run_step &lt;span class="nb"&gt;install  &lt;/span&gt;bash &lt;span class="nt"&gt;-lc&lt;/span&gt; &lt;span class="s1"&gt;'cd /tmp/pf-repo &amp;amp;&amp;amp; npm ci --no-audit --no-fund'&lt;/span&gt;
run_step build    bash &lt;span class="nt"&gt;-lc&lt;/span&gt; &lt;span class="s1"&gt;'cd /tmp/pf-repo &amp;amp;&amp;amp; npm run build --if-present'&lt;/span&gt;
run_step smoke    bash &lt;span class="nt"&gt;-lc&lt;/span&gt; &lt;span class="s1"&gt;'cd /tmp/pf-repo &amp;amp;&amp;amp; npm test -- --bail'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running it five times on cold caches gives you something to argue with, and jq turns the pile into one sorted answer per step. The &lt;code&gt;blocking&lt;/code&gt; field is my own blunt weighting: median milliseconds multiplied by the failure rate plus a small floor, so a fast step that always fails still surfaces near the top.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;'
  group_by(.step)
  | map({step: .[0].step,
         runs: length,
         fails: (map(select(.status=="fail")) | length),
         p50_ms: (map(.ms) | sort | .[length/2 | floor])})
  | map(. + {blocking: (.p50_ms * ((.fails / .runs) + 0.1) | floor)})
  | sort_by(-.blocking)
'&lt;/span&gt; /tmp/preflight-&lt;span class="k"&gt;*&lt;/span&gt;.jsonl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I do not treat that score as science, because it is not; it is a way to end a meeting where everyone has a different favourite step. If the top row is your &lt;code&gt;install&lt;/code&gt; step and it fails half the time, you have learned more in one minute than a week of reading complaints. Where is the signal in your own setup, honestly?&lt;/p&gt;

&lt;h3&gt;
  
  
  The one fix that mattered was a version check
&lt;/h3&gt;

&lt;p&gt;On one repo, five of seven failed runs died in the same place, and it was not the build. People were installing dependencies against a Node major that the lockfile did not want, and the error only appeared after a three-minute download. We had written three careful paragraphs about the right version in the README, which is a lovely artefact that nobody reads at nine in the morning. The fix was a step zero that costs two seconds and names the culprit out loud.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# step 0 — fail in two seconds instead of twenty minutes&lt;/span&gt;
&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/^nodejs/ {print $2}'&lt;/span&gt; .tool-versions&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;have&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;node &lt;span class="nt"&gt;--version&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/^v//'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$have&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$want&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"node &lt;/span&gt;&lt;span class="nv"&gt;$have&lt;/span&gt;&lt;span class="s2"&gt; != pinned &lt;/span&gt;&lt;span class="nv"&gt;$want&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;64
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wrap that in &lt;code&gt;run_step versions&lt;/code&gt; and it lands in the log with almost no milliseconds and a clean fail, which is exactly what you want from a gate. The lesson generalises beyond Node: move the constraint out of prose and into a check that runs before the expensive work. A fast loud failure is a gift, while a slow quiet one is a support ticket with extra steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is it me, or is it the repo?
&lt;/h3&gt;

&lt;p&gt;The hardest question in a first fifteen minutes is whether the breakage belongs to the repository or to the laptop. Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode's free model access and free server option are supplied by the operator and are the two pieces I reach for here; the terms, quotas and availability change over time, so check the current documentation before you plan around them. The server option is the useful one for triage: run the identical &lt;code&gt;preflight.sh&lt;/code&gt; inside a known-good baseline, then diff two files instead of reading tea leaves.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# local run, then the same script against the baseline host&lt;/span&gt;
./preflight.sh /tmp/pf-local.jsonl
&lt;span class="c"&gt;# run it again in the baseline environment you have access to&lt;/span&gt;

diff &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'"\(.step) \(.status)"'&lt;/span&gt; /tmp/pf-local.jsonl | &lt;span class="nb"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'"\(.step) \(.status)"'&lt;/span&gt; /tmp/pf-baseline.jsonl | &lt;span class="nb"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the baseline passes everything and your laptop fails at &lt;code&gt;install&lt;/code&gt;, stop rewriting the README and go look at your toolchain. The second piece is smaller: when a log is genuinely ambiguous, a model call can sort it into toolchain, network, permissions or unknown. I keep a keyword classifier as the default path and treat the model as an optional hook, so the script still works offline and on a locked-down runner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;classify&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;log&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qiE&lt;/span&gt; &lt;span class="s1"&gt;'EACCES|permission denied'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$log&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;permissions&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qiE&lt;/span&gt; &lt;span class="s1"&gt;'ECONNRESET|ETIMEDOUT|ENOTFOUND'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$log&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;network&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qiE&lt;/span&gt; &lt;span class="s1"&gt;'engine|unsupported|version'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$log&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;toolchain&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;echo &lt;/span&gt;unknown   &lt;span class="c"&gt;# swap for a model call; read endpoint and model id from current docs&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What the numbers cannot tell you
&lt;/h3&gt;

&lt;p&gt;One run is an anecdote, so I only trust a row after about five cold-cache runs, and I only trust a failure rate after roughly eight fresh machines across at least two operating systems. Wall clock on a shared CI box is noisy, and a baseline environment drifts away from your users' laptops over time, which makes it a reference point rather than proof. The score also cannot see humiliation: a step that takes four seconds and prints a wall of red text will lose you a contributor just as fast as a slow one.&lt;/p&gt;

&lt;p&gt;So who should skip all of this? If your setup is already one command that passes on a clean container, you have solved the problem this article measures, and a devcontainer or Nix flake probably buys you more than a timing harness. If your dependencies are private or your network policy forbids an external baseline, you can still run the per-step timing locally, but the diff loses most of its value. Small teams of two or three contributors may also find that a pinned toolchain file alone gets them most of the way, with no JSONL at all.&lt;/p&gt;

&lt;p&gt;If you want a baseline machine without provisioning one, the free server option is the cheapest way I have found to answer the is-it-me question, so run the script twice and compare two boring files. Then ask the only question that matters: which single step would you delete from a stranger's first fifteen minutes today?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devtools</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Slow Step Should Never Wait Its Turn</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Mon, 26 Oct 2026 11:42:33 +0000</pubDate>
      <link>https://dev.to/codejs_1959/the-slow-step-should-never-wait-its-turn-5am2</link>
      <guid>https://dev.to/codejs_1959/the-slow-step-should-never-wait-its-turn-5am2</guid>
      <description>&lt;p&gt;Fifteen minutes is a budget, not a vibe, and the fastest way to blow it is to schedule your slowest step last. I keep meeting the same first-run experience: three correct steps, written in the wrong order, defended by nobody. The steps are usually fine — the schedule is what leaks. So this is a post about ordering, with a harness you can point at your own setup tonight.&lt;/p&gt;

&lt;p&gt;The loop I care about is deliberately small: prove the key works, get one reply from a model, get one answer from a server. Free model access and a free server option make that loop cheap enough to restart five times in an afternoon, which is exactly what a first-run budget needs. The setup I am pointing the harness at here uses MonkeyCode's free model access and free server option. Disclosure: This article was prepared as part of MonkeyCode's product outreach.&lt;/p&gt;

&lt;p&gt;Here is the naive version, and yes, it is the version I wrote first. Three awaits in a column, each waiting for the one above it, even though almost nothing about them depends on each other.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// serial.mjs — the shape of most quickstarts, including my own first draft&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;checkAuth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;     &lt;span class="c1"&gt;// one request&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;askModel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;      &lt;span class="c1"&gt;// one request, but only after auth resolves&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;health&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;bootServer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;    &lt;span class="c1"&gt;// a cold start, but only after the model replies&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ask the obvious question out loud: does the server boot need the model reply before it can begin? No. Does auth need the boot at all? Also no. So why is the slowest stage sitting at the bottom of the file? Because it was written last, and nobody re-read the file with a stopwatch in hand.&lt;/p&gt;

&lt;p&gt;That is the whole teardown, and the fix that mattered is one line of scheduling, not three new features.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// the fix: the slow, idempotent stage starts first and runs beside the fast one&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bootResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authResult&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;boot&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;()]);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modelResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;authResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;skipped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auth failed first&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wall clock goes from &lt;code&gt;boot + auth + model&lt;/code&gt; to roughly &lt;code&gt;max(boot, auth) + model&lt;/code&gt;. If your cold boot is the long pole — and it usually is, because a free server option will happily cold-start for you — you have just removed almost all of it from the critical path. If the boot is not the long pole, the harness tells you so and you delete that branch. Either way you stop guessing.&lt;/p&gt;

&lt;p&gt;Here is the full harness, no dependencies, Node 18 or newer. Every stage has a name, its own budget, and an abort handle, so a hang becomes a labelled timeout instead of a silent stare at the terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cp"&gt;#!/usr/bin/env node
&lt;/span&gt;&lt;span class="c1"&gt;// envelope.mjs — three first-run probes under one 15-minute envelope.&lt;/span&gt;
&lt;span class="c1"&gt;// Env: MODEL_BASE_URL, MODEL_API_KEY, MODEL_NAME, SERVER_HEALTH_URL&lt;/span&gt;
&lt;span class="c1"&gt;// Optional: BUDGET_AUTH_MS, BUDGET_MODEL_MS, BUDGET_BOOT_MS, ENVELOPE_BUDGET_MS, LEDGER_PATH&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;writeFileSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BUDGET_MS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ENVELOPE_BUDGET_MS&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;STAGE_BUDGET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BUDGET_AUTH_MS&lt;/span&gt;  &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BUDGET_MODEL_MS&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;boot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BUDGET_BOOT_MS&lt;/span&gt;  &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;t0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elapsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;t0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Never throws: a failed stage returns a failed row, so one error cannot hide the others.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;budget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;STAGE_BUDGET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;budget&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;started&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;started&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AbortError&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timeout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;started&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MODEL_API_KEY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MODEL_API_KEY unset — this is an auth failure, not a model failure&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MODEL_BASE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/models`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`auth endpoint said &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MODEL_BASE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/chat/completions`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MODEL_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MODEL_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Reply with the single word: ready&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`model endpoint said &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;boot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SERVER_HEALTH_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`health endpoint said &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Schedule: the slow idempotent stage goes first, beside auth. Model waits for auth only.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bootRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authRow&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;boot&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;()]);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modelRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;authRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;skipped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auth failed first&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;authRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;modelRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bootRow&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padEnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;  &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padEnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;  &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;padStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;ms  &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bad&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`\nverdict: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bad&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;outside — &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;bad&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;inside the envelope&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`wall clock &lt;/span&gt;&lt;span class="p"&gt;${(&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;s of a &lt;/span&gt;&lt;span class="p"&gt;${(&lt;/span&gt;&lt;span class="nx"&gt;BUDGET_MS&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;s budget`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LEDGER_PATH&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;envelope-ledger.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;runAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;totalMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exitCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bad&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;BUDGET_MS&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it with the cheapest possible inputs, and do not commit the key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;MODEL_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;MODEL_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;SERVER_HEALTH_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HEALTH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;MODEL_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.secrets/trial-key&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; node envelope.mjs

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"exit code: &lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="c"&gt;# 1 means the envelope was missed, and the rows say where&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output format matters more than my numbers, which I am not going to pretend to know for your network. It looks roughly like this, with &lt;code&gt;NN.Ns&lt;/code&gt; standing in for whatever your machine actually does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auth    ok         NN.Ns
model   ok         NN.Ns
boot    ok         NN.Ns

verdict: inside the envelope
wall clock NN.Ns of a 900s budget
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the part I actually care about: the ledger file behind the verdict. One run is an anecdote, but six runs on six mornings answer a much better question — is this stage slow, or was that one Tuesday slow? Compare &lt;code&gt;totalMs&lt;/code&gt; across the file and read the row that keeps winning. Blaming the model for a cold boot is the most common misdiagnosis I have watched people make, and it costs them an afternoon of prompt tweaking that was never going to help.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Long pole in the ledger&lt;/th&gt;
&lt;th&gt;First move&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;boot&lt;/code&gt; dominates&lt;/td&gt;
&lt;td&gt;Start it first and let it warm while auth runs; consider a keep-warm ping before you sit down&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;auth&lt;/code&gt; dominates&lt;/td&gt;
&lt;td&gt;Cache the exchange locally for the session; a 401 loop is not a model problem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;model&lt;/code&gt; dominates&lt;/td&gt;
&lt;td&gt;Shrink the probe to a two-token reply before you judge latency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nothing dominates, but the total is over&lt;/td&gt;
&lt;td&gt;The envelope is wrong, not the provider; write down what you actually need&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few honest limitations, because this harness is a stopwatch, not a diagnosis. Free tiers queue, rate-limit, and change underneath you, so a green run today is evidence about today only. The parallel branch assumes your server boot is idempotent and safe to cancel, which is not true for every service, and behind a corporate proxy you are mostly measuring the proxy. And the script proves which stage is slow without telling you why it is slow — you still have to read a log.&lt;/p&gt;

&lt;p&gt;Who should skip this? Teams on committed infrastructure with stable latency and real monitoring, because the ledger is a downgrade from what they already have. Anyone whose first fifteen minutes are dominated by a compiler toolchain, since the harness will just confirm a truth you already resent. And anyone who wants to turn this into a leaderboard — it measures your network and your order of operations, not the quality of anybody's model.&lt;/p&gt;

&lt;p&gt;If you want something free to break while you test the schedule, the free model access and free server option are a fair place to point the harness; check the current quotas in your own dashboard rather than trusting a number in a blog post, mine included. Then move one &lt;code&gt;await&lt;/code&gt; and watch the wall clock change shape, because that is the whole trick.&lt;/p&gt;

</description>
      <category>dx</category>
      <category>devtools</category>
      <category>productivity</category>
      <category>ai</category>
    </item>
    <item>
      <title>Stopwatch the Cold Start, Not the Model</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Tue, 20 Oct 2026 09:48:52 +0000</pubDate>
      <link>https://dev.to/codejs_1959/stopwatch-the-cold-start-not-the-model-2j5e</link>
      <guid>https://dev.to/codejs_1959/stopwatch-the-cold-start-not-the-model-2j5e</guid>
      <description>&lt;h1&gt;
  
  
  Stopwatch the Cold Start, Not the Model
&lt;/h1&gt;

&lt;p&gt;The first conclusion I want to hand you is uncomfortable: in a brand-new AI-assisted workspace, the model is almost never your bottleneck. The cold start is, and it stays invisible because nobody writes timestamps down. Ask ten developers where their first fifteen minutes went and nine will blame the model, which is a polite way of saying they never measured. I now treat onboarding as a latency problem with a p99 budget, and the change that moved the number most was not a smarter model at all.&lt;/p&gt;

&lt;p&gt;So let me show you the instrument first and the opinions second, because an argument about free tiers without a stopwatch is just vibes with a changelog attached. The script below is deliberately boring: it records a millisecond timestamp before and after each step you already run, then prints the deltas. Nothing in it is specific to any vendor, which is exactly why it survives the next time you switch tools.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# scripts/cold-start.sh — timestamp every step of a fresh workspace bootstrap.&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LOG&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;cold&lt;/span&gt;&lt;span class="p"&gt;-start.tsv&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

now_ms&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'import time; print(int(time.time() * 1000))'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
mark&lt;span class="o"&gt;()&lt;/span&gt;   &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\t%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;now_ms&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

mark &lt;span class="s2"&gt;"boot"&lt;/span&gt;

mark &lt;span class="s2"&gt;"install:start"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  ./scripts/install-toolchain.sh&lt;span class="p"&gt;;&lt;/span&gt;      mark &lt;span class="s2"&gt;"install:end"&lt;/span&gt;
mark &lt;span class="s2"&gt;"deps:start"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     npm ci &lt;span class="nt"&gt;--no-audit&lt;/span&gt; &lt;span class="nt"&gt;--no-fund&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          mark &lt;span class="s2"&gt;"deps:end"&lt;/span&gt;
mark &lt;span class="s2"&gt;"auth:start"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     ./scripts/check-creds.sh&lt;span class="p"&gt;;&lt;/span&gt;             mark &lt;span class="s2"&gt;"auth:end"&lt;/span&gt;
mark &lt;span class="s2"&gt;"model:start"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    ./scripts/smoke-task.sh &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/smoke.patch&lt;span class="p"&gt;;&lt;/span&gt; mark &lt;span class="s2"&gt;"model:end"&lt;/span&gt;

mark &lt;span class="s2"&gt;"apply:start"&lt;/span&gt;
git apply &lt;span class="nt"&gt;--check&lt;/span&gt; /tmp/smoke.patch &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git apply /tmp/smoke.patch
mark &lt;span class="s2"&gt;"apply:end"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The report is six lines of awk, and I keep it in the repo next to the script so the numbers are comparable across weeks. Reading a table beats remembering a feeling, especially when the feeling is "it seemed slow".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'\t'&lt;/span&gt; &lt;span class="s1"&gt;'
  NR==1 { start=$1; prev=$1; printf "%9s %9s  %s\n", "delta_ms", "total_ms", "step"; next }
  { printf "%9d %9d  %s\n", $1-prev, $1-start, $2; prev=$1 }
'&lt;/span&gt; cold-start.tsv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run it three times, with the container thrown away between runs, because a warm cache will happily lie to you. I use a throwaway image precisely so that "it works on my machine" cannot creep into the measurement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in &lt;/span&gt;1 2 3&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;:/w"&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; /w node:22-slim ./scripts/cold-start.sh
  &lt;span class="nb"&gt;mv &lt;/span&gt;cold-start.tsv &lt;span class="s2"&gt;"cold-&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;.tsv"&lt;/span&gt;
 &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The variance between those three runs matters more than the absolute number, and I learned that the hard way. If run one and run three differ by more than about thirty percent on the same step, you are not measuring your setup, you are measuring the network. That distinction changes what you fix, so please do not skip the repetition.&lt;/p&gt;

&lt;p&gt;Here is how I bucket the deltas, and these thresholds are opinions you should tune rather than laws. The point of a table like this is to stop the argument before it starts, because "slow" is not actionable and "install took 412 seconds" is.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bucket&lt;/th&gt;
&lt;th&gt;Typical symptom&lt;/th&gt;
&lt;th&gt;Act when&lt;/th&gt;
&lt;th&gt;First move&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;install&lt;/td&gt;
&lt;td&gt;toolchain download, version drift&lt;/td&gt;
&lt;td&gt;over ~4 min&lt;/td&gt;
&lt;td&gt;pin versions, cache the layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;deps&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;npm ci&lt;/code&gt; re-resolving a lockfile&lt;/td&gt;
&lt;td&gt;over ~2 min&lt;/td&gt;
&lt;td&gt;commit the lockfile, use &lt;code&gt;ci&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;auth&lt;/td&gt;
&lt;td&gt;silent 401 retries, wrong profile&lt;/td&gt;
&lt;td&gt;any failure&lt;/td&gt;
&lt;td&gt;fail loudly, never retry blind&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;model&lt;/td&gt;
&lt;td&gt;first round trip, no output&lt;/td&gt;
&lt;td&gt;over ~60 s&lt;/td&gt;
&lt;td&gt;send a trivial prompt, not the real task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;apply&lt;/td&gt;
&lt;td&gt;patch rejected, whitespace noise&lt;/td&gt;
&lt;td&gt;any failure&lt;/td&gt;
&lt;td&gt;shrink the smoke task to one file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The one fix that mattered for me was not faster inference; it was turning the cold start into a single warm step that either succeeds or fails in under a minute. I put a &lt;code&gt;warm&lt;/code&gt; target in the Makefile, and now every new machine, container, or teammate runs exactly one command before touching the real work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;warm&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    ./scripts/install-toolchain.sh &lt;span class="nt"&gt;--pinned&lt;/span&gt;
    npm ci &lt;span class="nt"&gt;--no-audit&lt;/span&gt; &lt;span class="nt"&gt;--no-fund&lt;/span&gt;
    ./scripts/smoke-task.sh &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null   &lt;span class="c"&gt;# cheap proof that creds and endpoint answer&lt;/span&gt;
    ./scripts/cold-start.sh &lt;span class="nt"&gt;--report&lt;/span&gt;      &lt;span class="c"&gt;# keeps the number honest over time&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why does a smoke task beat a real task as the first move? Because a real task mixes three unknowns at once — your prompt, the endpoint, and the repository state — and you cannot debug three unknowns together. A tiny task that touches one file separates them, and it costs almost nothing when the environment is already broken. I have wasted entire afternoons on prompts that were fine while the credentials were quietly stale, and I would like you to skip that afternoon.&lt;/p&gt;

&lt;p&gt;When the smoke patch never lands, the debugging order is fixed and short in my notes. First check that the endpoint answers a trivial request without touching the repo, then check that the repo is clean enough to accept a patch, then check the diff size. Ninety percent of the time the failure is in step one or two and the prompt was never the problem at all.&lt;/p&gt;

&lt;p&gt;This is the workflow where MonkeyCode's free model access and free server option are genuinely relevant, and I want to be precise about what that means. &lt;em&gt;Disclosure: This article was prepared as part of MonkeyCode's product outreach.&lt;/em&gt; The operator describes the project as open source with a free tier that includes model access and a server option, and the free allowance has been quoted to me as ten million tokens; quotas move, so verify the current figure on the project's own page before you plan around it. What I actually used it for is unglamorous and that is the point: I ran the timed loop above against a hosted workspace so the install and dependency buckets were not my laptop's problem. Because the source is public, you can read what the server does with your prompt before you send anything you would not paste into a ticket.&lt;/p&gt;

&lt;p&gt;Who should not use this approach? Anyone who needs a contractual SLA, anyone whose cold start is dominated by a proprietary build cache the free tier cannot see, and anyone working in a regulated environment where the answer to "where did the prompt go" must be a document rather than a repository. A free tier is a fine place to measure friction and a poor place to promise uptime, and confusing those two is how teams end up with a very confident incident report.&lt;/p&gt;

&lt;p&gt;So before you benchmark another model, time your own first fifteen minutes and write the numbers into a file that other people can read. My guess is that your biggest bucket is not the one you have been blaming, and you will only find out if something is counting. If you want a place to run the stopwatch without provisioning anything, the free workspace is a reasonable first target — just bring the timer with you.&lt;/p&gt;

</description>
      <category>dx</category>
      <category>opensource</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
    <item>
      <title>The First Fifteen Minutes: One Preflight, One Fix</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Mon, 19 Oct 2026 08:30:32 +0000</pubDate>
      <link>https://dev.to/codejs_1959/the-first-fifteen-minutes-one-preflight-one-fix-1j7</link>
      <guid>https://dev.to/codejs_1959/the-first-fifteen-minutes-one-preflight-one-fix-1j7</guid>
      <description>&lt;p&gt;The thing that decides whether a new AI coding setup survives contact with a real repo is almost never the model — it is whether the first fifteen minutes end in something you can verify. I have been tearing down first-run experiences for a while now, and the pattern keeps repeating: people lose the quarter hour to a handful of small uncertainties, then blame the tool for being slow. So instead of reviewing features, let me show you the stopwatch I use, the friction log it produces, and the single fix that actually moved the needle for me.&lt;/p&gt;

&lt;p&gt;The premise is boring on purpose. You cannot improve a setup you have not timed, and most of us never time anything; we just remember that it "felt slow" and carry that feeling into the next tool we try. A clock turns a vague feeling into a row in a file, and a row in a file turns into an argument you can act on. Isn't that a better basis for a decision than vibes?&lt;/p&gt;

&lt;h2&gt;
  
  
  Clock every step before you judge anything
&lt;/h2&gt;

&lt;p&gt;I start by sourcing a tiny timing helper into the shell session, so every step I take gets a timestamp without me thinking about it. The script is deliberately dumb: it records elapsed seconds and a note, and it never tries to be clever about what a "step" is. You decide that, and you decide it before you start, which is the whole point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# clock.sh — source me, then call mark after every step&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-uo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CLOCK_LOG&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;friction&lt;/span&gt;&lt;span class="p"&gt;.tsv&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'step\tseconds\tnote\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;CLOCK_T0&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;

mark&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;now&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;now&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\t%s\t%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;now &lt;span class="o"&gt;-&lt;/span&gt; CLOCK_T0&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'[%3ss] %s %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;now &lt;span class="o"&gt;-&lt;/span&gt; CLOCK_T0&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then a first run looks like this, with the setup command replaced by whatever the project's README currently tells you to run — I am not going to paste an install line I cannot verify against today's docs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ./clock.sh
mark &lt;span class="s2"&gt;"account"&lt;/span&gt;     &lt;span class="s2"&gt;"signed in, key copied to clipboard"&lt;/span&gt;
mark &lt;span class="s2"&gt;"install"&lt;/span&gt;     &lt;span class="s2"&gt;"ran the documented setup command"&lt;/span&gt;
mark &lt;span class="s2"&gt;"server"&lt;/span&gt;      &lt;span class="s2"&gt;"free server option selected"&lt;/span&gt;
mark &lt;span class="s2"&gt;"first-prompt"&lt;/span&gt; &lt;span class="s2"&gt;"opened editor, sent a trivial question"&lt;/span&gt;
mark &lt;span class="s2"&gt;"first-diff"&lt;/span&gt;  &lt;span class="s2"&gt;"got a change I could actually apply"&lt;/span&gt;
column &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;$'&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last row is the only one I really care about, because everything before it is preparation and everything after it is work. If &lt;code&gt;first-diff&lt;/code&gt; lands at two minutes or at nineteen, that number describes the tool's design more honestly than any feature list. What would your own log say about the last three tools you tried?&lt;/p&gt;

&lt;p&gt;Here is the shape of my log from a recent run through an AI coding setup that offers free model access plus a free server option. These rows are illustrative of the format, not measurements from a controlled benchmark — rerun the clock yourself before you quote anything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;step          seconds  note
account       42       key copied, no docs needed
install       96       two commands, one PATH fix
server        18       free server option, no card prompt
config        151      env vars invented by me, not documented
first-prompt  63       worked, but model choice unclear
first-diff    644      mostly me re-reading setup notes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at the two biggest numbers and notice that neither of them is the model. Config and re-reading together account for more than half the wall clock, and both are pure friction: nothing was learned, nothing was produced, and no token was spent. That is the leak I keep finding in onboarding budgets, and it is cheap to plug.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one fix that mattered
&lt;/h2&gt;

&lt;p&gt;The fix was not a tutorial and it was not better documentation — it was a preflight script that refuses to let me start until the boring questions are already answered. Instead of discovering an unreachable endpoint inside my editor, I discover it in a two-second terminal command that prints a clear OK or FAIL per check. Failure moves to the place where failure is cheap.&lt;/p&gt;

&lt;p&gt;I wrote the version below against a generic OpenAI-compatible shape, so treat the paths as placeholders to adjust against the project's current API reference rather than gospel. The structure is what matters: check reachability, check that your credential is accepted, and exit non-zero so a wrapper can stop you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# preflight.sh — proposed, adapt paths to the current API docs&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MC_BASE_URL&lt;/span&gt;:?export&lt;span class="p"&gt; MC_BASE_URL first&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MC_API_KEY&lt;/span&gt;:?use&lt;span class="p"&gt; a shell env var, never a committed file&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;fail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'%{http_code}'&lt;/span&gt; &lt;span class="nt"&gt;--max-time&lt;/span&gt; 5 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MC_BASE_URL&lt;/span&gt;&lt;span class="s2"&gt;/health"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"200"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"OK   health"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;else &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"FAIL health: &lt;/span&gt;&lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;fail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi

if &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--max-time&lt;/span&gt; 5 &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$MC_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MC_BASE_URL&lt;/span&gt;&lt;span class="s2"&gt;/v1/models"&lt;/span&gt; | jq &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'.data | length &amp;gt; 0'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"OK   credential accepted"&lt;/span&gt;
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"FAIL credential or endpoint moved"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;fail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$fail&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wire it into a make target so the gate is the path of least resistance, and so skipping it requires a deliberate keypress rather than an oversight. A gate you have to think about bypassing is a gate that holds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;preflight&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;./preflight.sh &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"stop here: fix the failing line before opening the editor"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nl"&gt;first-run&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;preflight&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"you have 15 minutes; make them count"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Does this feel like overhead? It is eleven lines and roughly two seconds, and in exchange the expensive part of your session starts with a known-good connection instead of an unknown one. That trade is the whole article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the free server option earns its place
&lt;/h2&gt;

&lt;p&gt;I run the preflight against the free server option because a remote endpoint fails in ways that are visible from curl, while a local misconfiguration tends to fail inside an editor where the error message is a shrug. Free model access is genuinely useful during the first fifteen minutes, when you are sending small, disposable prompts to learn the interface rather than doing serious work. The caveat is that free capacity is exactly the kind of thing that changes without notice, so check the project's current page for today's limits instead of trusting a number someone quoted last month. Want the details straight from the source? The MonkeyCode project page is the only place worth trusting for current availability.&lt;/p&gt;

&lt;p&gt;Disclosure: This article was prepared as part of MonkeyCode's product outreach.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your situation&lt;/th&gt;
&lt;th&gt;Preflight against free server&lt;/th&gt;
&lt;th&gt;Preflight locally&lt;/th&gt;
&lt;th&gt;Skip preflight entirely&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Evaluating an unfamiliar tool&lt;/td&gt;
&lt;td&gt;Yes — fastest signal&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repo contains secrets or customer data&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reproducible CI check&lt;/td&gt;
&lt;td&gt;Unreliable; no SLA&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One-off throwaway script&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Overkill&lt;/td&gt;
&lt;td&gt;Acceptable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency-sensitive workflow&lt;/td&gt;
&lt;td&gt;Measure first, then decide&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Limits, and who should walk away
&lt;/h2&gt;

&lt;p&gt;This workflow measures setup friction, and it says nothing useful about code quality, model reasoning, or cost per task — do not let a clean preflight talk you into trusting output you have not reviewed. Free tiers change, endpoints move, and a health check that passed at nine in the morning proves nothing at five in the afternoon, so the script is a gate, not a guarantee. If you handle regulated data, need an SLA, or cannot tolerate a third-party endpoint seeing your prompts, this whole approach is the wrong shape for you and a local-only setup is the honest answer.&lt;/p&gt;

&lt;p&gt;Who should try it? Anyone who has ever said "this tool feels slow" without a number behind the claim, and anyone onboarding a teammate who will ask the same five questions next Monday. Run the clock once, keep the log in the repo next to the Makefile, and let the friction show itself. Fifteen minutes of measurement buys you a much better argument than fifteen minutes of opinion — and honestly, what else were you going to do with that quarter hour?&lt;/p&gt;

</description>
      <category>devtools</category>
      <category>productivity</category>
      <category>ai</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Teardown: The First Fifteen Minutes, Minus One Step</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Sun, 18 Oct 2026 07:11:02 +0000</pubDate>
      <link>https://dev.to/codejs_1959/teardown-the-first-fifteen-minutes-minus-one-step-2fob</link>
      <guid>https://dev.to/codejs_1959/teardown-the-first-fifteen-minutes-minus-one-step-2fob</guid>
      <description>&lt;p&gt;The fix that mattered most in my last onboarding teardown was not a better doc, a faster model, or a smarter plugin — it was deleting a step. I keep relearning this, and I keep being surprised by how much of the first fifteen minutes evaporates once you remove the one gate that everyone hits before they can even ask a question. So this is not a review of a tool; it is the ledger I use to find that gate, and one concrete result from running it against a free-tier setup.&lt;/p&gt;

&lt;p&gt;Half the posts I scrolled past this week were some version of "AI fatigue", and honestly, a lot of that fatigue is setup fatigue wearing a costume. You read a launch post, you carve out twenty minutes, and then you spend eighteen of them picking a provider, finding a key, and discovering your laptop's GPU situation. Isn't that the real tax? Not the inference bill — the pre-inference ceremony.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instrument before you optimize
&lt;/h2&gt;

&lt;p&gt;You cannot shorten a wait you never measured, so the first artifact is a ledger, not a benchmark. I stamp each step into a CSV as it happens, then read it back once the first accepted edit lands; the numbers are wall clock, not impressions. Run it in the same shell session where you are actually doing the setup, so the timestamps reflect the real pauses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# first15.sh — stamp each onboarding step, then read the ledger back.&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail
&lt;span class="nv"&gt;LEDGER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LEDGER&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;first15&lt;/span&gt;&lt;span class="p"&gt;.csv&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

stamp&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s,%s,%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; +%FT%TZ&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LEDGER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
  &lt;/span&gt;start&lt;span class="p"&gt;)&lt;/span&gt;  : &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LEDGER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; stamp &lt;span class="s2"&gt;"clock-start"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
  mark&lt;span class="p"&gt;)&lt;/span&gt;   stamp &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;3&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
  report&lt;span class="p"&gt;)&lt;/span&gt; python3 - &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LEDGER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;PY&lt;/span&gt;&lt;span class="sh"&gt;'
import csv, sys
from datetime import datetime

rows = list(csv.DictReader(open(sys.argv[1])))
if len(rows) &amp;lt; 2:
    sys.exit("not enough stamps to report")

parse = lambda ts: datetime.fromisoformat(ts.replace("Z", "+00:00"))
start = parse(rows[0]["ts"])
prev = start
print(f'{"step":&amp;lt;26}{"delta_s":&amp;gt;9}{"total_s":&amp;gt;9}  note')
for r in rows[1:]:
    t = parse(r["ts"])
    print(f'{r["step"]:&amp;lt;26}{(t-prev).total_seconds():&amp;gt;9.1f}'
          f'{(t-start).total_seconds():&amp;gt;9.1f}  {r["note"]}')
    prev = t
print(f'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;total: {(prev-start).total_seconds():.1f}s across {len(rows)-1} transitions')
&lt;/span&gt;&lt;span class="no"&gt;PY
&lt;/span&gt;    &lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"usage: &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; start|mark|report [step] [note]"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;2 &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A typical session looks like &lt;code&gt;./first15.sh start "free-tier trial"&lt;/code&gt;, then a &lt;code&gt;mark&lt;/code&gt; for each gate you pass: account, key, endpoint, first reply, first file changed. Here is the shape of the output I care about — schematic, not a measurement of your machine or anyone else's:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;step                        delta_s  total_s  note
account-and-key                63.0     63.0  email verify included
endpoint-reachable              4.0     67.0  smoke request
first-reply                    19.0     86.0  one-line prompt
first-accepted-edit           241.0    327.0  wrong file, then right file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at where the minutes actually go. In my runs the account-and-key step is roughly a minute and the first reply is seconds, but the stretch between "it answered" and "it changed something I kept" is where four out of five minutes disappear — and that gap is a comprehension problem, not a latency problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gate nobody logs
&lt;/h2&gt;

&lt;p&gt;When I wrote the ledger down honestly, the biggest stall had a boring name: choosing what to ask first. People install the thing, get a cheerful reply, then freeze, because the blank prompt is a decision and decisions are slow. The fix was not a prompt library and it was definitely not a longer quickstart; it was constraining step four to a single named file with a single named change, so the first request has an obvious right answer.&lt;/p&gt;

&lt;p&gt;The second gate was infrastructure — not speed, just existence. Every "do you have a GPU, a card, a quota?" question adds a branch to the first fifteen minutes, and branches are where people leave. That is where a free server option does real work in a teardown like this: it removes a branch rather than shortening one. &lt;code&gt;Disclosure: This article was prepared as part of MonkeyCode's product outreach.&lt;/code&gt; The project is open source and, per its own materials at the time of writing, advertises free model access plus a free server option and a free token allowance on the order of ten million tokens; I would re-check that allowance on the project page before you plan around it, because published quotas move.&lt;/p&gt;

&lt;p&gt;What I will not tell you is how fast it is, which models sit behind it, or how it compares on a benchmark — I did not measure any of that here, and a number I invented would be worse than no number. What I can say is what the ledger says: with the hardware question removed, my schematic run loses its second-longest stall, and the remaining time goes into the only step that teaches you anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  A smoke test before you commit twenty minutes
&lt;/h2&gt;

&lt;p&gt;The cheapest habit I have is a thirty-second reachability check before installing anything. Substitute the base URL and model id from your own account page — the path below is illustrative, and I have not run this exact request against the current API, so treat it as a template rather than documentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://&amp;lt;endpoint-from-your-dashboard&amp;gt;"&lt;/span&gt;
curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /tmp/first.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'http=%{http_code} total=%{time_total}s\n'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$MONKEYCODE_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{"model":"&amp;lt;model-id&amp;gt;","messages":[{"role":"user","content":"reply with the word ready"}]}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BASE_URL&lt;/span&gt;&lt;span class="s2"&gt;/v1/chat/completions"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that prints a 200 and the word &lt;code&gt;ready&lt;/code&gt;, the next fifteen minutes belong to your problem instead of your plumbing. If it prints a 401, you found the gate in one command instead of nine, and that is a win even though nothing worked. Keep &lt;code&gt;/tmp/first.json&lt;/code&gt; around; when something breaks later, the diff between a working reply and a broken one is usually one header.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who should walk away
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your situation&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;Solo exploration, throwaway repo, no card&lt;/td&gt;
&lt;td&gt;Worth the fifteen minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regulated code, audit trail, SSO requirements&lt;/td&gt;
&lt;td&gt;Check the terms first, probably not&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You need pinned model versions for reproducibility&lt;/td&gt;
&lt;td&gt;Not without a fallback path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Your team already has an approved provider&lt;/td&gt;
&lt;td&gt;Adding a second one is a cost, not a feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You want a benchmark winner&lt;/td&gt;
&lt;td&gt;This article will not give you one&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That last row matters. Free access and a free server are availability claims, not quality claims, and a teardown that measures onboarding friction says nothing about whether the output is any good on your codebase. If your code cannot leave your network, no amount of free is free.&lt;/p&gt;

&lt;p&gt;If you keep any kind of onboarding ledger for your own projects, run it once on the tool you already pay for before you add another one. You might find that the step you were about to document is the step you should have deleted — and isn't that a cheaper fix than writing the doc?&lt;/p&gt;

</description>
      <category>devtools</category>
      <category>ai</category>
      <category>productivity</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Onboarding Budgets Leak Before the First Commit</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Fri, 16 Oct 2026 05:13:04 +0000</pubDate>
      <link>https://dev.to/codejs_1959/onboarding-budgets-leak-before-the-first-commit-4615</link>
      <guid>https://dev.to/codejs_1959/onboarding-budgets-leak-before-the-first-commit-4615</guid>
      <description>&lt;p&gt;The fifteen minutes you budget for a new AI coding setup rarely die from a missing feature, and they almost never die from a slow model. They die from three unnamed waits: a key you hold but have nowhere to put, a command that technically succeeds while producing nothing you can verify, and the absence of any rule telling you when to stop. I have watched those three waits eat entire afternoons, and I stopped trusting my memory about them a long time ago.&lt;/p&gt;

&lt;p&gt;So this is not another review of what a tool can do. It is a measurement instrument you can run in your own terminal, plus the one fix that actually moved the needle for me. Call it a friction ledger, and yes, it is less exciting than a benchmark table, which is exactly why it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Name the three leaks before you time anything
&lt;/h2&gt;

&lt;p&gt;Placement friction is the cost of getting a credential, an endpoint, and a model identifier into the same place at the same time. Verification friction is the cost of turning a successful HTTP response into something a human can inspect and replay. Recovery friction is everything you pay after a mistake, including the four minutes you spend wondering whether the mistake was yours or the tool's.&lt;/p&gt;

&lt;p&gt;None of those show up in a "time to first token" chart, because a token is not a diff and a 200 is not evidence. What do you actually have after the happy path returns? If the answer is "a feeling," you have measured nothing, and the next person on your team will pay the same tax you just paid.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ledger itself: forty lines of bash
&lt;/h2&gt;

&lt;p&gt;A friction ledger records one row per step, with a monotonic-ish millisecond timestamp at the start and the end of that step. The point is not precision; the point is capturing the shape of the wait while you are still annoyed by it. Save this as &lt;code&gt;friction.sh&lt;/code&gt;, then &lt;code&gt;chmod +x friction.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;LEDGER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LEDGER&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;./friction.csv&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;STATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;STATE&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;./.friction.state&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

now_ms&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt; +%s%3N&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

init&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LEDGER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'step,start_ms,end_ms,elapsed_ms,outcome,note\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LEDGER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

start&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  init
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;step&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;:?usage:&lt;span class="p"&gt; friction.sh start &amp;lt;step&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$step&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;now_ms&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$STATE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"started: &lt;/span&gt;&lt;span class="nv"&gt;$step&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

end&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;outcome&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;note&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;started_step started_at
  &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; started_step started_at &amp;lt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$STATE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s,%s,%s,%s,%s,"%s"\n'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$started_step&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$started_at&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;now_ms&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;now_ms&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; started_at &lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$outcome&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$note&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LEDGER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$started_step&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;now_ms&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; started_at &lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;ms (&lt;/span&gt;&lt;span class="nv"&gt;$outcome&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

summary&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR&amp;gt;1 {t+=$4; n++} END {printf "%d steps, %d ms total, %.0f ms mean\n", n, t, t/n}'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LEDGER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage is deliberately boring, because a tool you have to think about is itself friction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./friction.sh start credential-placement
&lt;span class="c"&gt;# ... paste the key, wire the endpoint, pick the model ...&lt;/span&gt;
./friction.sh end ok &lt;span class="s2"&gt;"key in env file, not shell history"&lt;/span&gt;

./friction.sh start first-verifiable-output
./friction.sh end blocked &lt;span class="s2"&gt;"response arrived, no artifact on disk"&lt;/span&gt;

./friction.sh summary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One honest caveat about the instrument: it measures your environment and your habits, not the provider's engineering. A slow step might be your proxy, your laptop, or a corporate VPN doing what corporate VPNs do, and the ledger will happily record that as the product's fault. Keep the &lt;code&gt;note&lt;/code&gt; column populated and you will at least remember which one it was.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where a free tier fits into this, without the fantasy math
&lt;/h2&gt;

&lt;p&gt;Free access changes the calculation because the cost of a wrong turn drops to nearly zero, which is precisely when people stop being careful. I have been using MonkeyCode, an open-source project whose operator describes it as offering free model access together with a free server option, and a free allowance he puts at ten million tokens. Disclosure: This article was prepared as part of MonkeyCode's product outreach.&lt;/p&gt;

&lt;p&gt;Because that arrangement is described to me rather than audited by me, I treat it the way I treat any free tier: useful for measuring, dangerous for assuming. Verify the current terms on the project page before you plan a sprint around them, since free allowances move and nobody owes you a grandfather clause.&lt;/p&gt;

&lt;p&gt;What the free server option is genuinely good for is the boring middle of the ledger. You want a place to run the first-verifiable-output step that is not your work laptop with three expired tokens in a config directory. What it is not good for is proving that your production topology will behave the same way, because it will not, and pretending otherwise is how teams ship surprises.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one fix that mattered
&lt;/h2&gt;

&lt;p&gt;Here is the change that cut my own ledger the most, and it had nothing to do with models. I collapsed three separate touchpoints — key, base URL, and model identifier — into one sourced file, so that there was exactly one place where a stale value could hide. Before that, every step in the ledger was quietly paying a config-hunting tax I had been blaming on the network.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ~/.config/ai/env.sh — sourced, never copy-pasted into a shell history&lt;/span&gt;
: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;AI_KEY&lt;/span&gt;:?set&lt;span class="p"&gt; AI_KEY before sourcing this file&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;AI_MODEL&lt;/span&gt;:?set&lt;span class="p"&gt; AI_MODEL before sourcing this file&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AI_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;AI_BASE_URL&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;https&lt;/span&gt;://your-endpoint-here&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;AI_KEY AI_MODEL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then a preflight that fails loudly and early, before the fifteen-minute clock starts rather than nine minutes into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;preflight&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;start elapsed
  &lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s%3N&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;v &lt;span class="k"&gt;in &lt;/span&gt;AI_BASE_URL AI_KEY AI_MODEL&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;!v&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"missing: &lt;/span&gt;&lt;span class="nv"&gt;$v&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;done
  &lt;/span&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'http %{http_code}\n'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$AI_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AI_BASE_URL&lt;/span&gt;&lt;span class="s2"&gt;/models"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;1
  &lt;span class="nv"&gt;elapsed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s%3N&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; start &lt;span class="k"&gt;))&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"preflight ok in &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;elapsed&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pair it with a step budget, and the ledger stops being a diary and becomes a decision. I use two thresholds: placement must finish inside one minute, and first verifiable output must finish inside five. If either blows past its budget twice in a row on the same step, I stop debugging and change the approach instead of the parameter.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ledger observation&lt;/th&gt;
&lt;th&gt;Read it as&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Placement under 60s, first artifact under 5 min&lt;/td&gt;
&lt;td&gt;The seam is clean&lt;/td&gt;
&lt;td&gt;Keep going, record the winning sequence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same step over budget twice&lt;/td&gt;
&lt;td&gt;Not a typo, a design gap&lt;/td&gt;
&lt;td&gt;Switch approach or switch tool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;blocked&lt;/code&gt; outcome with &lt;code&gt;no artifact on disk&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Verification friction&lt;/td&gt;
&lt;td&gt;Write the expected file path down before the call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fast &lt;code&gt;ok&lt;/code&gt; rows, unreadable later&lt;/td&gt;
&lt;td&gt;Theater&lt;/td&gt;
&lt;td&gt;Add the &lt;code&gt;note&lt;/code&gt; column or stop measuring&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Limitations, and who should skip this entirely
&lt;/h2&gt;

&lt;p&gt;The numbers in that table are placeholders from my own template and not measurements of any specific product, so please do not quote them as benchmarks. A CSV kept by one developer also hides the failures that matter at scale, like the teammate who never tells you they were blocked for three days. And if your code cannot leave your network for compliance reasons, no free server option is a relevant input, no matter how generous the allowance looks.&lt;/p&gt;

&lt;p&gt;Who should not use this? Teams that need contractual guarantees, anyone unwilling to re-run the ledger after an environment change, and people who want a benchmark score to paste into a slide. A single measurement run is theater; the ledger earns its keep when it is boring and repeated.&lt;/p&gt;

&lt;p&gt;If you want a cheap place to try the whole loop, the MonkeyCode project is open source and its free model access plus free server option is a reasonable sandbox for the first two ledger steps. Bring a stopwatch, trust your own rows over anyone's summary, and tell me which step ate your fifteen minutes — I suspect it was never the model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# the whole article in two lines&lt;/span&gt;
./friction.sh start first-verifiable-output &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./friction.sh end ok &lt;span class="s2"&gt;"artifact on disk, path recorded"&lt;/span&gt;
./friction.sh summary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ai</category>
      <category>opensource</category>
      <category>productivity</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Time to First Verifiable Diff</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Thu, 15 Oct 2026 11:23:50 +0000</pubDate>
      <link>https://dev.to/codejs_1959/time-to-first-verifiable-diff-3jpa</link>
      <guid>https://dev.to/codejs_1959/time-to-first-verifiable-diff-3jpa</guid>
      <description>&lt;h1&gt;
  
  
  Time to First Verifiable Diff
&lt;/h1&gt;

&lt;p&gt;The onboarding number I actually trust is not how long the install takes, but how long it takes before a tool hands me a diff I can verify. Total install time tells you that something went wrong somewhere in the chain; it never tells you which link to cut, and that gap is why so many evaluations end in vibes. I keep running into conversations about AI fatigue, and I think a large share of it is not model quality at all, but onboarding friction that nobody bothered to localize. So I built a small instrument that localizes it, and the number it produces changed how I trial tools.&lt;/p&gt;

&lt;p&gt;Earlier I wrote about clocking the first fifteen minutes, and I still believe the clock is worth holding. The problem is that a duration without a per-step ledger is a verdict, not a diagnosis. If the whole run took eleven minutes, do I fix the signup, the runtime, the docs, or my own prompt? You cannot answer that from a single stopwatch reading, and guessing wrong is how people conclude a tool is slow when it was actually their network.&lt;/p&gt;

&lt;h2&gt;
  
  
  Record the steps, then rank them
&lt;/h2&gt;

&lt;p&gt;The harness below is a template, not a product wrapper, and I am not going to pretend I measured anything for you here. Every number it prints comes from your own first run, on your own machine, against your own network, which is the only measurement that transfers. It writes one JSON line per phase transition, so the ledger is append-only and replayable after the fact. Here is the recorder, with placeholder commands where the vendor-specific work would go.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# step-ledger.sh — illustrative harness, not tied to any vendor CLI.&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail
&lt;span class="nv"&gt;LEDGER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LEDGER&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;./onboarding.jsonl&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;RUN_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; +%Y%m%dT%H%M%SZ&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

stamp&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c"&gt;# stamp &amp;lt;step&amp;gt; &amp;lt;phase&amp;gt; [note]&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'{"run":"%s","step":"%s","phase":"%s","ts":"%s","note":"%s"}\n'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RUN_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; +%Y-%m-%dT%H:%M:%S.%3NZ&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;3&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LEDGER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
begin&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; stamp &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; begin &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
end&lt;span class="o"&gt;()&lt;/span&gt;   &lt;span class="o"&gt;{&lt;/span&gt; stamp &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; end   &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

begin signup  &lt;span class="s2"&gt;"no card, real email"&lt;/span&gt;
&lt;span class="c"&gt;# ... whatever the tool asks for ...&lt;/span&gt;
end   signup

begin &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"docs command, copy-pasted verbatim"&lt;/span&gt;
&lt;span class="c"&gt;# ... paste the installer line from the docs ...&lt;/span&gt;
end   &lt;span class="nb"&gt;install

&lt;/span&gt;begin firsttask &lt;span class="s2"&gt;"ask for a one-line fix, nothing else"&lt;/span&gt;
&lt;span class="c"&gt;# ... send the request and wait for output ...&lt;/span&gt;
end   firsttask
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rules matter more than the code. Copy the documented command verbatim, because paraphrasing from memory hides the moment the docs failed. Tag each step with a short note about what happened, since the note is what turns a bar on a chart into an actionable complaint. Never edit the ledger by hand, because a hand-edited measurement is a story, and stories are exactly what this exercise exists to replace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turn the log into a ranked complaint
&lt;/h2&gt;

&lt;p&gt;A raw log is still just a log, so the second half of the instrument ranks the steps by cost and prints a total. Sorting descending is deliberate: the top line is the fix you should try first, and everything below it is noise until that one is addressed. The printer is short enough to read in one pass, which matters because you will want to trust it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;ledger.py — turn onboarding.jsonl into a ranked friction ledger.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%Y-%m-%dT%H:%M:%S.%fZ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;span class="n"&gt;runs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]].&lt;/span&gt;&lt;span class="nf"&gt;setdefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;step&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{})[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;phase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;note&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ranked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phases&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;begin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;phases&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;end&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;phases&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phases&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;end&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;phases&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;begin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;total_seconds&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;ranked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phases&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;begin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ranked&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;7.1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;note&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;7.1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s  TOTAL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it and you get a shape like &lt;code&gt;signup 3.9s&lt;/code&gt;, &lt;code&gt;install 214.0s&lt;/code&gt;, &lt;code&gt;firsttask 41.2s&lt;/code&gt;, and those figures are illustrative of the format rather than a claim about any tool. The interesting part is how often the largest line is not the model, not the signup, but the local runtime install. It is also the step people skip when they retry, which is why they conclude the second attempt was faster when the machine was simply warm.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one fix that mattered
&lt;/h2&gt;

&lt;p&gt;Here is the change that moved the number more than any other, and it has nothing to do with which model answered. Replace the first task with a task that must end in a verifiable diff, using a two-file fixture you already know the correct answer to. A chat reply is unfalsifiable in the first fifteen minutes; a diff against a failing test is not. So I start every trial like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; fixture &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;fixture
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'def add(a, b):\n    return a - b\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; calc.py
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'from calc import add\nassert add(2, 3) == 5\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; test_calc.py
python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest &lt;span class="nt"&gt;-q&lt;/span&gt;   &lt;span class="c"&gt;# fails: this failure is your baseline&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the first prompt is boring on purpose: fix the failing assertion and return a patch, not an explanation. You apply the patch, you rerun pytest, and the run either passed or it did not, which means the onboarding measurement ends with a boolean instead of an impression. I label this metric time to first verifiable diff, and it collapses a dozen vague first-run complaints into one number you can compare across tools without arguing about style. Should you trust a tool that needs forty minutes to reach a green test more than one that needs four, when the second one also answers every unrelated question beautifully?&lt;/p&gt;

&lt;h2&gt;
  
  
  Where a zero-cost tier changes the ledger
&lt;/h2&gt;

&lt;p&gt;Two of the steps in that ledger are pure overhead, and neither of them teaches you anything about the tool. One is billing setup, and the other is provisioning a machine before you can see whether the edit loop even works for you. MonkeyCode, which I encountered as an open-source coding agent project, addresses both by being reachable without a payment step and by offering a free server option, so the first pass of the ledger can run without either of those entries. &lt;em&gt;Disclosure: This article was prepared as part of MonkeyCode's product outreach.&lt;/em&gt; The free model access is the part that keeps an evaluation cheap enough to abandon, which honestly is the property I care about most.&lt;/p&gt;

&lt;p&gt;I am not going to quote a token allowance, a model list, or a server specification here, because I cannot verify those from where I sit and free tiers change quietly. The project's own pages are the only honest source for current limits and terms, so read them before you build anything that depends on them. Treat the free path as a way to get a first data point, never as the foundation of a pipeline whose reproducibility you actually need. A ledger you can rerun tomorrow is worth more than a discount you cannot predict.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who should skip this approach
&lt;/h2&gt;

&lt;p&gt;If your code cannot leave your network, the free-server path is irrelevant and the harness only helps you measure friction you were never going to pay. If your team needs SSO, audit logs, and deterministic model pinning, optimizing first-run minutes is optimizing the wrong axis, and the ledger will just document the wrong constraint. And if you already have a working loop you trust, the instrument is unnecessary, because you are no longer onboarding and there is nothing here to localize.&lt;/p&gt;

&lt;p&gt;For everyone else, the transferable artifact is about sixty lines of shell and Python, and it survives whichever tool you abandon next quarter. If a zero-cost first pass makes that instrument cheap to run, start there, keep your own copy of the numbers, and let the top line of your ledger decide what you fix tomorrow.&lt;/p&gt;

</description>
      <category>devtools</category>
      <category>productivity</category>
      <category>ai</category>
      <category>testing</category>
    </item>
    <item>
      <title>Clock the First Fifteen Minutes</title>
      <dc:creator>Quinn Wang</dc:creator>
      <pubDate>Sun, 11 Oct 2026 21:30:56 +0000</pubDate>
      <link>https://dev.to/codejs_1959/clock-the-first-fifteen-minutes-1m3i</link>
      <guid>https://dev.to/codejs_1959/clock-the-first-fifteen-minutes-1m3i</guid>
      <description>&lt;p&gt;The first fifteen minutes of an AI coding session are the product test, not a warm-up you can skip. If you cannot name the friction in that window, you should not trust the diff that follows it. I treat that opening as a drill, because chatter is an easy way to lose an afternoon without a diff. Have you ever closed a setup feeling productive, only to find that nothing reviewable landed in the tree?&lt;/p&gt;

&lt;p&gt;Models keep getting louder in public, while the first edit on a real repository stays oddly quiet. I would run those minutes like a short drill, with a clock I own and a budget set before any prompt. A model can sound clever and still burn the window on a wrong directory or a silent authentication hop. The one fix I would bet on is not a smarter prompt, and it is not a longer context window either.&lt;/p&gt;

&lt;p&gt;It is a friction ledger that refuses the first real edit until four cheap checks each have a timestamp. Think of that opening quarter hour as a kitchen in the tense minutes just before service begins. You can hire a famous chef and still miss the plate if the oven is cold and the ticket is blank. The coding assistant is the chef in this picture, and the oven is the working tree you meant to change.&lt;/p&gt;

&lt;p&gt;The ticket is the task you can replay later, and the clock is the only judge that does not flatter you. I split the drill into four beats, and I give each beat a budget before I type a single prompt. Reach means I can talk to a model without guessing which door actually opened underneath me. Place means the process cwd is the repository I intend to change, not a parent folder I opened by habit.&lt;/p&gt;

&lt;p&gt;Read means git status is clean enough that a later diff belongs to this session and not to yesterday. Touch means a throwaway branch can be created and deleted, which puts git state back where it started. If any beat blows the budget I chose, I stop and write down why before I try the next door. Have you ever restarted a tool three times and still been unable to say which minute failed?&lt;/p&gt;

&lt;p&gt;An unlabeled restart just moves the same fog forward, which is how a short setup becomes an afternoon. The script below is a proposal I have not executed against any vendor dashboard, so the numbers are your budgets. It writes a ledger outside the work tree, then checks place, read, and a reach note you measured by hand. Only after those beats pass does it create and delete a scratch branch, so touch is proof rather than a guess.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cp"&gt;#!/usr/bin/env node
&lt;/span&gt;&lt;span class="c1"&gt;// Unexecuted proposal: time the first fifteen minutes. Not a vendor benchmark.&lt;/span&gt;
&lt;span class="c1"&gt;// node dx-drill.mjs --reach-ms 8000 --reach-note "plain reply, no file yet"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;execFileSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:child_process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;appendFileSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tmpdir&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:os&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;join&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;budgetMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;place&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BUDGET_PLACE_MS&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;30000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BUDGET_READ_MS&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;30000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;reach&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BUDGET_REACH_MS&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;120000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;touch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BUDGET_TOUCH_MS&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;180000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ledgerPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FRICTION_LEDGER&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tmpdir&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;friction-ledger.jsonl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;started&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;stamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;measuredMs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wallMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;started&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elapsedMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;measuredMs&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;wallMs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;budget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;budgetMs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;elapsedMs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;wallMs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;budgetMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;budget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;overBudget&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;budget&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;elapsedMs&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;budget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nf"&gt;appendFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ledgerPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;overBudget&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stop:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pass:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;elapsedMs&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ms&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;git&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gitArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;execFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gitArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cwd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;git&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rev-parse&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--show-toplevel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nf"&gt;stamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;place&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cwd&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;git&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--porcelain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nf"&gt;stamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;read&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;clean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reachMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--reach-ms&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reachNote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--reach-note&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;stamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reach&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isFinite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reachMs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;reachMs&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;reachNote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;reachNote&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;reachMs&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dx-drill-scratch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;git&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;switch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="nx"&gt;created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;git&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;branch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--show-current&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;created&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;git&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;switch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nf"&gt;git&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;branch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-D&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;stamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;touch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;created&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;restored&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;created&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ledger&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ledgerPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save that file as &lt;code&gt;dx-drill.mjs&lt;/code&gt; at the repository root, and run it only after you have timed a plain reply. Pass your own latency and a short confession in &lt;code&gt;--reach-ms&lt;/code&gt; and &lt;code&gt;--reach-note&lt;/code&gt; after that plain reply. If you launch the file from a parent folder, place should fail before any scratch branch is created. I would read the stop line, then open the ledger path the script prints, instead of guessing from memory.&lt;/p&gt;

&lt;p&gt;The reach flag is a confession, not a probe, and that is why the script refuses a note that is too short. A green HTTP status can still mean you reached a model you did not choose, or a queue that will miss your window. I want the note to say what I asked, how long the reply took, and whether any file or branch changed. Have you watched a fluent answer land in the wrong tree and then blamed the weights for a path mistake?&lt;/p&gt;

&lt;p&gt;The operator asked me to time this drill against MonkeyCode's free model access and its free server option. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The operator also describes the project as open source, which you should confirm on the project page itself. I do not have a measured quota, a hardware sheet, or a permanence promise here, so I will not invent figures.&lt;/p&gt;

&lt;p&gt;I would not start that session by asking for a feature, a migration, or a clever refactor of a large file. I would start the ledger, confirm which door I opened, and only then ask for the same scratch branch and nothing else. If the free server path spends most of the fifteen minutes on provisioning, that is a useful result rather than a model failure. If the free model path answers quickly but never changes git state, the ledger keeps that failure apart from the server story.&lt;/p&gt;

&lt;p&gt;The same drill still helps if you swap the product name for any other assistant you already have installed. That portability is deliberate, because a first-fifteen-minute test that only flatters one brand is not a test. I also will not claim that this setup is faster, cheaper, or more accurate than tools I have not measured side by side. Have you noticed how a smooth landing page can hide a slow first edit, and how a plain ledger makes that swap visible?&lt;/p&gt;

&lt;p&gt;I would not use this approach if you need a compliance sign-off, a published benchmark, or a promise about free-tier duration. The script does not prove quality, safety, or cost, and the local budgets mostly catch a hung git. Teams without local git, a current Node, or &lt;code&gt;git switch&lt;/code&gt; should skip it, because the drill assumes a repository you control. If your host forbids new branches, pick another reversible touch, and do not let the assistant choose that touch.&lt;/p&gt;

&lt;p&gt;If a previous scratch branch is still around, touch will fail, and that failure is the drill doing its job. Point &lt;code&gt;FRICTION_LEDGER&lt;/code&gt; outside the repository, delete the leftover branch, and rerun &lt;code&gt;git status --porcelain&lt;/code&gt; until it is empty. Then time the two doors separately, once for free model access and once for the free server option, without averaging them. A blended score is how the first fifteen minutes become a story instead of a record you can argue with later.&lt;/p&gt;

&lt;p&gt;If you want a next step, run the drill once on the free model door and the free server door you already have. Keep the ledger, then decide from your own timestamps whether those first fifteen minutes earned a second session. I would rather read a messy ledger than hear that a demo felt smooth, because smoothness is not a diff. The clock will not make the model wiser, but it will stop you from paying an afternoon for a setup you never named.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>git</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
