<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Cezar Cocu</title>
    <link>https://cezarcocu.com/</link>
    <description>Personal site of Cezar Cocu, software engineer and veteran. Writing on coding agents, Python, backend systems, applied AI/ML, and lessons from past lives.</description>
    <language>en-us</language>
    <lastBuildDate>Thu, 07 May 2026 01:25:44 GMT</lastBuildDate>
    <atom:link href="https://cezarcocu.com/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>I'll believe it when I see it</title>
      <link>https://cezarcocu.com/blog/ill-believe-it-when-i-see-it</link>
      <guid isPermaLink="true">https://cezarcocu.com/blog/ill-believe-it-when-i-see-it</guid>
      <pubDate>Tue, 05 May 2026 00:00:00 GMT</pubDate>
      <description>A small story about a broken demo in '18, and a much bigger question about what happens when the next demo doesn't break.</description>
      <content:encoded><![CDATA[<p>Jack Clark's <a href="https://importai.substack.com/p/import-ai-455-automating-ai-research">recent essay on RSI for AI R&#x26;D being realistic by '26</a> reminded me of a funny, and I claim very relevant, story:</p>
<p>Back in ye olde '17 to '18, when rumors of these large language models were filling the halls, a co-worker pulled me over to show me an early model. He got my attention with the claim that <em>"...soon everyone will have a tutor!"</em>.</p>
<p>A BOLD claim, indeed!</p>
<p>I scurried to his desk excited and watched him paste in the most convoluted cli call I had ever seen. He hit enter, and after what felt like an awkward eternity, the response came back … and of course exhibiting that classic llm artifact where the model just repeats the same sentence over and over. His terminal filled up quickly and he had to ctrl+c out.</p>
<p><em>"I'm not sure what happened, it just worked before! WTH?"</em></p>
<p>I of course gave him some corporate words of assurance, but I quipped privately to myself: <em>a personal tutor for everyone?! I'll believe it when I see it!</em></p>
<p>Alas, here we are…</p>
<p>That moment now feels like a fitting metaphor for where we are with rsi and intelligence. Regardless of someone's personal p(rsi) or exact timeline, it's clearly going to become reality soon, and it will impact all of our work and how we individually provide value to society and our community. Even with how unhelpful current models can still be at times :-).</p>
<p>However, I'm not really sure anyone can really guarantee anything of real substance about whether it will be good, bad, or what the effects will look like, even if their own efforts are pointed in a certain direction. I think the best we can do is to honestly point to the trend line itself, and that's about it?</p>
<p>For me, I think this is at the heart of why I find it quite unnerving.</p>]]></content:encoded>
    </item>
    <item>
      <title>Works on my Claude Code (and now yours)</title>
      <link>https://cezarcocu.com/blog/setting-up-claude-code-on-the-web-correctly</link>
      <guid isPermaLink="true">https://cezarcocu.com/blog/setting-up-claude-code-on-the-web-correctly</guid>
      <pubDate>Mon, 04 May 2026 00:00:00 GMT</pubDate>
      <description>How to configure Claude Code on the web so it actually mirrors your real dev environment: onboarding script, environment quirks, and the LSP wiring that matters most.</description>
      <content:encoded><![CDATA[<h2>TL;DR</h2>
<ul>
<li>Claude Code has a lot of confusing options if you want to use cloud agents. <code>--remote</code>, <code>--teleport</code>, others?!</li>
<li>You probably want just plain "Claude Code on the web" using their sandbox.</li>
<li>The "Claude Code on the web" <a href="https://code.claude.com/docs/en/claude-code-on-the-web#the-cloud-environment">sandbox</a> needs configuring.</li>
<li>The same sandbox backs Claude on the web, the mobile app, and <code>/ultraplan</code> / <code>/ultrareview</code> runs from the CLI. Configure once, covered everywhere on any sandbox.</li>
<li>Coding agents <em>should</em> check their own work using your real toolchain (type checker, linter, compiler), at the specific versions your team runs. Without that you may not be able to reproduce bugs or cause easily avoidable PR check failures, or even subject your code to <a href="https://docs.litellm.ai/blog/security-update-march-2026">supply chain attacks</a>.</li>
</ul>
<a href="https://x.com/gvanrossum/status/2049990552322777253" target="_blank" rel="noopener noreferrer">
  <img src="/blog/setting-up-claude-code-on-the-web-correctly/guido-tweet.png" alt="Tweet from Guido van Rossum: Everybody is adding a feature where you can manage your agents from your phone. Don't use it. You'll just get even more addicted, and will burn out even quicker." />
</a>
<p>Probably wise words. Alas we proceed:</p>
<p>Ok, forget about <code>claude --remote</code> or <code>claude --teleport</code> or w/e shenanigans. I am calling it now, just invest the time for you and your team to set up your repo for a generic sandbox environment. Thank me later.</p>
<p>Anthropic generously right now provides a Ubuntu 24.04 box running as root with ~4 vCPUs, 16 GB RAM, and 30 GB disk and the price is ~right~ (free for now).</p>
<p>If you follow these steps you should get yourself a hermetic, reproducible sandbox environment that you can take anywhere, and I claim, on any harness.</p>
<p>Ok, hopefully I have you convinced, if yes and <em>if you'd rather just hand this to a coding agent, jump to <a href="#hand-this-to-your-agent">Hand this to your agent</a>.</em></p>
<p>There is a problem though: the <a href="https://code.claude.com/docs/en/claude-code-on-the-web#installed-tools">sandbox</a> created by Claude Code will very likely <em>not</em> have the exact environment for your project.</p>
<p>Why bother?</p>
<p>If you need convincing: if you instructed your agent to actually use your project's type checker, linter, and compiler to verify its own work (run tests?), then these should mirror your local versions exactly. Pin the exact Python runtime you currently use. i.e. 3.12.12, not "3.12"; pin your linter and type checker to whatever your team runs. If the agent's runtime drifts from yours, bugs that depend on patch behavior won't reproduce. On the other hand, package managers like <code>uv</code> are the exception (unless <a href="https://docs.astral.sh/uv/guides/integration/docker/#installing-uv">you're building a docker image</a>): those you want at the latest stable release.</p>
<p>I set this up for my python-based project, and there are a handful of non-obvious things that will make you scratch your head. This is the guide I wish existed:</p>
<h2>The strategy</h2>
<p>You'll need 3 things: an environment startup script, a SessionStart CC hook and a small script you paste into the environment:</p>
<ol>
<li><strong>Checked in cloud setup script</strong> (the textbox; references a script in your repo). Cached for ~7 days. Use it for what your current dev env already has — system libs, exact language runtimes, package managers, LSP server binaries, etc.</li>
<li><strong><code>SessionStart</code> hook</strong> (committed to <code>.claude/settings.json</code>). Runs every session startup/resume on cloud <em>and</em> local. Use it for project deps that move with the codebase — <code>uv sync</code>, <code>npm install</code>, etc.</li>
</ol>
<p>For python, Warning: If you bake <code>uv sync</code> into the cached setup script and your cloud sessions keep resuming against the snapshot for a week while teammates bump <code>uv.lock</code> on <code>main</code>. Also: the hook running locally means the agent <em>never starts</em> on stale deps when you switch git branches.</p>
<h2>Run /web-setup (if your admin enabled it)</h2>
<p>If you haven't already, run <code>/web-setup</code> to share your local GH cli token w/ Claude :yikes_emoji or better yet install the Anthropic app in your GitHub repo or org.</p>
<h2>SessionStart hook vs setup script</h2>
<p><a href="https://code.claude.com/docs/en/claude-code-on-the-web#setup-scripts-vs-sessionstart-hooks">Anthropic's table</a> covers the details. But the tl;dr is</p>
<ul>
<li>If my dev environment already has it, use the setup script to install the exact version.</li>
<li>If it changes when teammates bump lockfiles, or other shenanigans, tell Claude to write you a <code>SessionStart</code> hook.</li>
</ul>
<p>So the setup script gets <code>apt</code>, <code>uv</code> itself, <code>mise</code>, pinned runtimes (<code>uv python install</code> / <code>mise install</code>), and any LSP server binaries you build from source. Hook gets <code>uv sync</code>, <code>mix deps.get</code>, <code>npm install</code>. The hook also runs locally, so make it fast on no-op — these three commands all are by default, but if you bolt on <code>mix compile</code> or a slow build step, gate it behind a stamp file or your laptop will pay the cost on every <code>claude</code> startup.</p>
<h2>The setup script</h2>
<p>The "Update cloud environment" dialog with Name, Network access, Environment variables, and Setup script fields You might feel the urge to paste your script straight into this textbox. Don't!</p>
<p>The docs hint that environment configs and scripts can be visible to anyone who can edit that environment. This might be an unreleased feature since I haven't been able to share them on either my Team or personal Max account, and the env isn't even linked to a specific GitHub repo, so each engineer ends up re-deriving the same setup from scratch.</p>
<p>The trick: the repo gets cloned first, then the setup script runs from the repo root, and only then does Claude Code launch. So you can keep your script in the repo, reference it from the textbox, and let it double as a local onboarding script for new engineers.</p>
<p>Put this in <code>scripts/onboard.sh</code> (toolchain only — no <code>uv sync</code> etc., that goes in the hook):</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">#!/usr/bin/env bash</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">set</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -euo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pipefail</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">REPO_ROOT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"$(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "$(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dirname</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">BASH_SOURCE</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">[0]}")/.." &#x26;&#x26; </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">pwd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">)"</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$REPO_ROOT</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Drop pre-installed Launchpad PPAs that 403 in the sandbox.</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">rm</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -f</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /etc/apt/sources.list.d/deadsnakes</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">*</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.list</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      /etc/apt/sources.list.d/ondrej</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">*</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.list</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> 2></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/dev/null</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ||</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Install or upgrade uv. The pre-installed version tends to lag.</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> !</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> command</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> uv</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/dev/null</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> 2>&#x26;1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">then</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  curl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -LsSf</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://astral.sh/uv/install.sh</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sh</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  export</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> PATH</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$HOME</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/.local/bin:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$PATH</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  uv</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> self</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> update</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --quiet</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ||</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">fi</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">uv</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> python</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # reads .python-version, pins exactly</span></span></code></pre>
<p>Then, in the setup script textbox, paste:</p>
<pre><code>bash "$(find /home /root -maxdepth 4 -name "onboard.sh" 2>/dev/null | head -1)"
</code></pre>
<p>The form above isn't project-specific: anyone on your team can use the same one-liner, every repo just needs an <code>onboard.sh</code>. Multi-repo sessions work too; find returns the first match (only that one runs).</p>
<p>(Hint to the Claude Code team: these env configs really ought to be shareable at the org level. No good reason every engineer has to hand-paste the same setup textbox.)</p>
<p>Project deps go in a <code>SessionStart</code> hook. Drop this into <code>.claude/settings.json</code>:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "hooks"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    "SessionStart"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: [{</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">      "matcher"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"startup|resume"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">      "hooks"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: [{</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        "type"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"command"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        "command"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"uv sync --all-groups"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      }]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
<p>For multi-runtime projects, point the hook at a <code>scripts/dev-setup.sh</code> instead and put your <code>uv sync &#x26;&#x26; mix deps.get &#x26;&#x26; npm install</code> in there — easier to read, easier to test locally.</p>
<h2>A few smaller things</h2>
<ul>
<li>The default <strong>Trusted</strong> network preset covers what you need (npm, PyPI, Docker Hub, GitHub releases, Ubuntu archives). Leave it.</li>
<li>Tools shipping their own CA bundles trip on the TLS proxy. We hit this with Erlang/Hex; the fix is <code>SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt</code> and <code>HEX_CACERTS_PATH=/etc/ssl/certs/ca-certificates.crt</code> in the env vars box.</li>
<li><code>rm -f</code> the deadsnakes and ondrej PPAs from <code>/etc/apt/sources.list.d/</code> in your <code>onboard.sh</code> before <code>apt-get update</code>. Those specific PPAs 403 in the sandbox even though Launchpad itself is in the Trusted allowlist; <code>uv python install</code> covers what deadsnakes would have given you anyway.</li>
<li>The env vars box is plaintext. No secrets. Fine for things like <code>LANG=en_US.UTF-8</code> (sandboxes sometimes start with <code>latin1</code>, which breaks tools expecting UTF-8).</li>
<li><code>gh</code> is <em>not</em> pre-installed (the docs say so, though I'd swear I've seen it on the image at some point). Add <code>apt install -y gh</code> to your <code>onboard.sh</code> if you need it, and set <code>GH_TOKEN</code> in the env vars box for authenticated calls. For public release assets, plain <code>curl</code> skips the install entirely.</li>
</ul>
<h2>LSPs</h2>
<p>Claude Code can use language servers in remote sessions, but only if every eng has them installed or they're declared in <code>.claude/settings.json</code> checked into your repo.</p>
<p><strong>For <a href="https://github.com/microsoft/pyright">pyright</a></strong> (CC's <a href="https://claude.com/plugins/pyright-lsp">official Python LSP</a>):</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "enabledPlugins"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    "python-lsp@claude-plugins-official"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
<p><strong>For <a href="https://github.com/astral-sh/ty">ty</a></strong> (Astral's type checker, faster than pyright on large codebases): ty ships through the Astral marketplace, which isn't registered by default:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "extraKnownMarketplaces"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    "astral-sh"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">      "source"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        "source"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"github"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        "repo"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"astral-sh/claude-code-plugins"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">      "autoUpdate"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "enabledPlugins"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    "astral@astral-sh"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
<p>For both LSPs you still need the <a href="http://pyright-langserver">pyright-langserver</a> or <a href="https://docs.astral.sh/uv/getting-started/installation/">ty dependencies</a> installed first on the local machine/sandbox, hence to ensure you have the script correct.</p>
<p>Honesty note: I've confirmed this pattern works in remote sessions for Python, TypeScript and Elixir LSPs but haven't verified others. Spot-check before relying on it.</p>
<hr>
<h2>Hand this to your agent</h2>
<pre><code>&#x3C;task>
Configure this repository to work cleanly with Claude Code on the web by creating a toolchain onboarding script, a project-hydration SessionStart hook, and an LSP configuration. Output the exact one-liner the user should paste into the Claude Code on the web "Setup script" textbox.
&#x3C;/task>

&#x3C;context>
Claude Code on the web runs in an Ubuntu 24.04 sandbox. Setup scripts run as root, and the repo lands at $HOME/{repo-name}. Outbound traffic goes through a TLS-inspecting proxy. The setup script runs from the repo root, before Claude Code launches, and its filesystem result is snapshotted and reused for ~7 days — so it's the right home for heavy toolchain installs. SessionStart hooks (declared in .claude/settings.json) fire on every session startup/resume in cloud AND local, so they're the right home for project hydration that needs to track lockfile changes (uv sync, mix deps.get, npm install).
&#x3C;/context>

&#x3C;steps>
1. Detect the project's languages and package managers by inspecting files at the repo root (.python-version, pyproject.toml, package.json, mix.exs, Cargo.toml, go.mod, etc.). State what you found before proceeding.

2. Create scripts/onboard.sh — TOOLCHAIN ONLY, no project hydration:
   - Shebang: #!/usr/bin/env bash
   - set -euo pipefail
   - Resolve REPO_ROOT from BASH_SOURCE[0] and cd into it.
   - Remove pre-installed Launchpad PPAs (deadsnakes, ondrej) from /etc/apt/sources.list.d/ before any apt-get call. Use 2>/dev/null || true so missing files don't abort.
   - Install or upgrade each *package manager* (uv, mise, nvm, etc.). Check for existing installation first; if present, run the tool's self-update with || true to swallow network errors.
   - Install each *runtime* (Python, Node, Elixir, etc.) at the exact version pinned by the project. Read pin files like .python-version, .tool-versions, .nvmrc, package.json's "engines". Do not install "latest" unless the project has no pin at all.
   - DO NOT include uv sync / mix deps.get / npm install / mix compile here — those go in the SessionStart hook in step 5.
   - Make every step idempotent.

3. If the project uses Erlang or Elixir, add these exports after the cd and before anything that fetches Hex packages:
   export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
   export HEX_CACERTS_PATH=/etc/ssl/certs/ca-certificates.crt
   These point Erlang's TLS layer at the system CA bundle, which trusts the sandbox's TLS proxy.

4. chmod +x scripts/onboard.sh.

5. Create or update .claude/settings.json with:
   - The LSP plugin(s) for the project's languages (python-lsp@claude-plugins-official for pyright, astral@astral-sh for ty — register the astral-sh marketplace under extraKnownMarketplaces — and analogues for other languages).
   - A hooks.SessionStart entry with matcher "startup|resume", running the project's hydration commands. Inline the command if it's a single line (e.g., uv sync --all-groups); for multi-runtime projects create scripts/dev-setup.sh that calls uv sync, mix deps.get, npm install in sequence and have the hook invoke that. Hook commands fire on every claude startup — keep them fast on no-op (uv sync, npm install, and mix deps.get all are by default).

6. Run scripts/onboard.sh, then run the hydration command from the hook, both locally. Verify both exit 0. If either fails, fix and re-run until they succeed.

7. Commit scripts/onboard.sh, .claude/settings.json, and any new hydration script.

8. Print a summary with three sections:
   - Detected toolchain
   - Paths of files created or modified
   - The exact one-liner the user should paste into the Claude Code on the web "Setup script" textbox: bash "$(find /home /root -maxdepth 4 -name "onboard.sh" 2>/dev/null | head -1)"
&#x3C;/steps>

&#x3C;constraints>
- Do not paste script contents into the textbox. Reference scripts/onboard.sh by path so it lives in version control and BASH_SOURCE[0] resolves correctly.
- Do not hardcode the repo name or $HOME into the textbox content. Use a `find`-based lookup so the one-liner stays portable across repos.
- Do not assume gh is authenticated. Use curl for public release downloads. For authenticated calls, instruct the user to set GH_TOKEN as an environment variable in the cloud environment.
- For Python versions, use uv python install rather than the deadsnakes PPA. The deadsnakes/ondrej PPA hosts return 403 in the sandbox even though Launchpad itself is in the Trusted allowlist.
- Always install the exact runtime version the project pins (.python-version, .tool-versions, .nvmrc, etc.). The agent and the team must run the same patch version for bug reports to reproduce.
- Project hydration commands (uv sync, mix deps.get, npm install) MUST go in the SessionStart hook, not in scripts/onboard.sh. The setup script is cloud-only and cached for ~7 days; baking hydration into it leaves resumed cloud sessions running against stale deps after teammates bump lockfiles.
&#x3C;/constraints>
</code></pre>
<hr>
<p><em>Co-written with Claude Opus 4.7.</em></p>]]></content:encoded>
    </item>
    <item>
      <title>Sometimes you don't get your ice cream at the end</title>
      <link>https://cezarcocu.com/blog/sometimes-you-dont-get-your-ice-cream-at-the-end</link>
      <guid isPermaLink="true">https://cezarcocu.com/blog/sometimes-you-dont-get-your-ice-cream-at-the-end</guid>
      <pubDate>Sat, 13 Dec 2025 00:00:00 GMT</pubDate>
      <description>Sometimes you don't get your ice cream at the end</description>
      <content:encoded><![CDATA[<p>Yes, it's cliché, but this was a lifetime ago. I had just turned nineteen a month prior.</p>
<p>It was the summer of '07, and this was <a href="https://en.wikipedia.org/wiki/Fort_Polk">Fort Polk, Louisiana</a>. It was a hot, humid, horrible feeling to just exist outside! The area has this type of weather where the air has a soupy texture and where it takes twice as many breaths as it should to extract one satisfying inhale.</p>
<p>A few of us were sitting over by a small military shop on-base, waiting to get picked up. We had finished our final training (JRTC) before getting our final deployment orders and were heading to our stationed base in Kentucky.</p>
<p>There were four of us from the same <a href="https://en.wikipedia.org/wiki/Company_(military_unit)#Modern_use">infantry company</a>, sitting on a bench. We had just gotten word of our <a href="https://en.wikipedia.org/wiki/Area_of_operations">Area of Operations</a> (AO) for our upcoming fifteen-month combat rotation. It was to be <a href="https://en.wikipedia.org/wiki/Al-Shu%27ala">somewhere in Baghdad</a>.</p>
<p>As you might imagine, some of us were feeling nervous, but didn't want to show it for fear of being seen as weak-minded.</p>
<p>I remember they had some benches and soft-serve ice cream. There were only two flavors, vanilla and chocolate, but that was more than enough for me. I remember wolfing it down; I had opted for the cone and it was glorious (still the way I prefer to enjoy my ice cream).</p>
<p>One of the younger sergeants, Sergeant Craig, came over and checked up on us. Craig couldn't have been older than twenty-one, just barely old enough to drink.</p>
<p>However, he and others had already been on multiple combat deployments and we really looked up to him. He was also goofy, and he had this warm, friendly smirk on his face. He was one of those people who didn't take life too seriously; The net effect of being around him was that he made you feel safe.</p>
<p>He must have noticed our nervousness, perhaps even misguided excitement, about deploying. All of this training and we were going to do this for real!</p>
<p>It was pretty obvious that at least some of us might not be coming back. I guess that it was a real possibility, but it wasn't something that I allowed myself to consider.</p>
<p>Craig sized us up and in his own goofy way wanted to lay some words of wisdom.</p>
<p><em>"Ohh, it's not going to be a big deal. I bet you we will be on a very big base with Burger King and all of that,"</em> he said. He noted that Baghdad seemed a step down from his previous deployment in the area which, at the time, was lovingly known as the <a href="https://en.wikipedia.org/wiki/Triangle_of_Death_(Iraq)">Triangle of Death</a>.</p>
<p><em>"On our last deployment, we had so much free time that I binge-watched shows and played video games most of the time. When we would patrol, we would just drive up and down this road the entire time,"</em> he continued.</p>
<p>One of the guys quipped, <em>"Well what if we get blown up or something? Won't that hurt?"</em></p>
<p><em>"If it hurts, then you're likely going to make it; otherwise, you're not going to know what fucking hit you!"</em> Craig quipped back.</p>
<p>I guess it all just fades to black.</p>
<p><em>"Also, when someone dies, it's totally not a big fucking deal. We will line everybody up and do our ceremony thing. We did our military funeral in shorts and flip-flops during our last deployment!"</em> He said this while holding onto his ice cream cone.</p>
<p>I remember us giving each other incredulous looks, but Craig's words resonated. This deployment just might end up not being a big deal.</p>
<hr>
<p>Craig's description of the deployment was nothing like what I experienced.</p>
<p>For starters, we had no free time. We were not on a large base; we were on a super small base (a <a href="https://en.wikipedia.org/wiki/Outpost_(military)">COP</a>) where the ~70 or so of us were pulling our own security while doing patrols and raids in between. We were too sleep-deprived and barely had time to play video games or anything of that sort. Most of our meals came in the form of <a href="https://en.wikipedia.org/wiki/Meal,_Ready-to-Eat">MREs</a> - no Burger King in sight.</p>
<p>If we got lucky, we would get the chance to ferry some captives to the Green Zone, in which case, there was indeed ice cream!</p>
<p>Then, eight months into the deployment, we found ourselves at Craig's funeral.</p>
<p>He had gotten shot in the head. How it happened is a story for another time.</p>
<p>For his funeral, military command decided to make a real dog and pony show out of it. Instead of doing the funeral at our small COP, they dragged our small company out to the main base so that all of the command leadership could attend.</p>
<p>I was told my uniform needed to be clean and crisp, which was a challenge on such short notice given the conditions at our tiny outpost.</p>
<p>It was a traditional military funeral and it was nice, but it was clear from the get-go that there were political undertones. Members of the press were there and so were high-ranking military officers with very little dirt on their uniforms.</p>
<p>As the ceremony was over, we all got into our trucks and headed back to our base.</p>
<p>I asked my Team Sergeant, Jarden, since we were at the main base, if we were going to stop for ice cream on our way back.</p>
<p>He turned slowly at me, <em>"What the fuck is wrong with you, Cocu?! Ice cream at a time like this?! You have got to be fucking kidding me! …There is something wrong with you!"</em></p>
<p>He hadn't been there when Craig told us his story. There was simply too much to explain. We left and convoyed back to our COP.</p>
<p>It was nighttime by now and I leaned back in the gunner's seat and stared out of the open-air top, looking up at the sky with my night vision goggles on. I expected to spot at least some stars but I couldn't spot any. It must have been overcast.</p>
<p>The truck was silent.</p>
<hr>
<p>Rest In Peace, Staff Sergeant <a href="https://thefallen.militarytimes.com/army-staff-sgt-clay-a-craig/3507740">Clay A. Craig</a>, Sergeant First Class <a href="https://www.campagnafuneralhomes.com/obituary/robert-jarden">Robert C. Jarden</a>.</p>
<hr>
<p><em>Thank you to my wife Mary, friends, and family for encouraging me to write this down, and for the feedback along the way.</em></p>]]></content:encoded>
    </item>
  </channel>
</rss>
