<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>PyPy (Posts about AI)</title><link>https://www.pypy.org/</link><description></description><atom:link href="https://www.pypy.org/categories/ai.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2026 &lt;a href="mailto:pypy-dev@pypy.org"&gt;The PyPy Team&lt;/a&gt; </copyright><lastBuildDate>Wed, 29 Apr 2026 06:51:59 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Using Claude to fix PyPy3.11 test failures securely</title><link>https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html</link><dc:creator>mattip</dc:creator><description>&lt;p&gt;I got access to Claude Max for 6 months, as a promotional move Anthropic made
to Open Source Software contributors. My main OSS impact is as a maintainer for
NumPy, but I decided to see what claude-code could to for PyPy's failing 3.11
tests. Most of these failures are edge cases: error messages that differ from
CPython, or debugging tools that fail in certain cases. I was worried about
letting an AI agent loose on my development machine. I noticed &lt;a class="reference external" href="https://patrickmccanna.net/a-better-way-to-limit-claude-code-and-other-coding-agents-access-to-secrets/"&gt;a post&lt;/a&gt; by
Patrick McCanna (thanks Patrick!) that pointed to using bubblewrap to
sandbox the agent. So I set it all up and (hopefully securely) pointed
claude-code at some tests.&lt;/p&gt;
&lt;!-- TEASER_END: Read more to find out how it went --&gt;
&lt;section id="setting-up"&gt;
&lt;h2&gt;Setting up&lt;/h2&gt;
&lt;p&gt;There were a few steps to make sure I didn't open myself up to obvious gotchas.
There are stories about agents wiping out data bases, or deleting mail boxes.&lt;/p&gt;
&lt;section id="bubblewrap"&gt;
&lt;h3&gt;Bubblewrap&lt;/h3&gt;
&lt;p&gt;First I needed to see what bubblewrap does. I followed the instructions in the
blog post to set things up with some minor variations:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_f47febb4d6864475889601b51c684d32-1" name="rest_code_f47febb4d6864475889601b51c684d32-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_f47febb4d6864475889601b51c684d32-1"&gt;&lt;/a&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;bubblewrap
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I couldn't run &lt;code class="docutils literal"&gt;bwrap&lt;/code&gt;. After digging around a bit, I found I needed to add
an exception for appamor on Ubuntu 24.04:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_5f45675cf72f4df29df88c4d18a23518-1" name="rest_code_5f45675cf72f4df29df88c4d18a23518-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5f45675cf72f4df29df88c4d18a23518-1"&gt;&lt;/a&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;bash&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'cat &amp;gt; /etc/apparmor.d/bwrap &amp;lt;&amp;lt; EOF&lt;/span&gt;
&lt;a id="rest_code_5f45675cf72f4df29df88c4d18a23518-2" name="rest_code_5f45675cf72f4df29df88c4d18a23518-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5f45675cf72f4df29df88c4d18a23518-2"&gt;&lt;/a&gt;&lt;span class="s1"&gt;abi &amp;lt;abi/4.0&amp;gt;,&lt;/span&gt;
&lt;a id="rest_code_5f45675cf72f4df29df88c4d18a23518-3" name="rest_code_5f45675cf72f4df29df88c4d18a23518-3" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5f45675cf72f4df29df88c4d18a23518-3"&gt;&lt;/a&gt;&lt;span class="s1"&gt;include &amp;lt;tunables/global&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_5f45675cf72f4df29df88c4d18a23518-4" name="rest_code_5f45675cf72f4df29df88c4d18a23518-4" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5f45675cf72f4df29df88c4d18a23518-4"&gt;&lt;/a&gt;
&lt;a id="rest_code_5f45675cf72f4df29df88c4d18a23518-5" name="rest_code_5f45675cf72f4df29df88c4d18a23518-5" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5f45675cf72f4df29df88c4d18a23518-5"&gt;&lt;/a&gt;&lt;span class="s1"&gt;profile bwrap /usr/bin/bwrap flags=(unconfined) {&lt;/span&gt;
&lt;a id="rest_code_5f45675cf72f4df29df88c4d18a23518-6" name="rest_code_5f45675cf72f4df29df88c4d18a23518-6" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5f45675cf72f4df29df88c4d18a23518-6"&gt;&lt;/a&gt;&lt;span class="s1"&gt;  userns,&lt;/span&gt;
&lt;a id="rest_code_5f45675cf72f4df29df88c4d18a23518-7" name="rest_code_5f45675cf72f4df29df88c4d18a23518-7" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5f45675cf72f4df29df88c4d18a23518-7"&gt;&lt;/a&gt;&lt;span class="s1"&gt;}&lt;/span&gt;
&lt;a id="rest_code_5f45675cf72f4df29df88c4d18a23518-8" name="rest_code_5f45675cf72f4df29df88c4d18a23518-8" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5f45675cf72f4df29df88c4d18a23518-8"&gt;&lt;/a&gt;&lt;span class="s1"&gt;EOF'&lt;/span&gt;
&lt;a id="rest_code_5f45675cf72f4df29df88c4d18a23518-9" name="rest_code_5f45675cf72f4df29df88c4d18a23518-9" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5f45675cf72f4df29df88c4d18a23518-9"&gt;&lt;/a&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apparmor_parser&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;/etc/apparmor.d/bwrap
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then &lt;code class="docutils literal"&gt;bwrap&lt;/code&gt; would run. It is all locked down by default, so I opened up some
exceptions. The arguments are pretty self-explanatory. Ubuntu spreads the
executables around the operating system, so I needed access to various
directories. I wanted a &lt;code class="docutils literal"&gt;/tmp&lt;/code&gt; for running pytest. I also wanted the prompt
to reflect the use of bubblewrap, so changed the &lt;code class="docutils literal"&gt;hostname&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-1" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-1"&gt;&lt;/a&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt; 'EOL' &amp;gt;&amp;gt; ./run_bwrap.sh&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-2" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-2"&gt;&lt;/a&gt;&lt;span class="s"&gt;  function call_bwrap() {&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-3" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-3" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-3"&gt;&lt;/a&gt;&lt;span class="s"&gt;    bwrap \&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-4" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-4" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-4"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /usr /usr \&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-5" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-5" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-5"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /etc /etc \&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-6" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-6" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-6"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /run /run \&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-7" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-7" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-7"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/lib /lib \&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-8" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-8" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-8"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/lib64 /lib64 \&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-9" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-9" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-9"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/bin /bin \&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-10" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-10" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-10"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --proc /proc \&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-11" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-11" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-11"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --dev /dev \&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-12" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-12" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-12"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --bind $(pwd) $(pwd) \&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-13" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-13" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-13"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --chdir $(pwd) \&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-14" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-14" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-14"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --unshare-user --unshare-pid --unshare-ipc --unshare-uts --unshare-cgroup \&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-15" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-15" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-15"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --die-with-parent \&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-16" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-16" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-16"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --hostname bwrap \&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-17" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-17" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-17"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --tmpfs /tmp \&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-18" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-18" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-18"&gt;&lt;/a&gt;&lt;span class="s"&gt;      /bin/bash "$@"&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-19" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-19" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-19"&gt;&lt;/a&gt;&lt;span class="s"&gt;  }&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-20" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-20" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-20"&gt;&lt;/a&gt;&lt;span class="s"&gt;EOL&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-21" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-21" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-21"&gt;&lt;/a&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-22" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-22" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-22"&gt;&lt;/a&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./run_bwrap.sh
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-23" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-23" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-23"&gt;&lt;/a&gt;call_bwrap
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-24" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-24" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-24"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# now I am in a sandboxed bash shell&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-25" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-25" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-25"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# play around, try seeing other directories, getting sudo, or writing outside&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-26" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-26" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-26"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# the sandbox&lt;/span&gt;
&lt;a id="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-27" name="rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-27" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5524b8cb3bdf46e9907a8f0b852aaec9-27"&gt;&lt;/a&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I did not do &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;--unshare-network&lt;/span&gt;&lt;/code&gt; since, after all, I want to use claude and
that needs network access. I did add rw access to &lt;code class="docutils literal"&gt;$(pwd)&lt;/code&gt; since I want it to
edit code in the current directory, that is the whole point.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="basic-claude"&gt;
&lt;h3&gt;Basic claude&lt;/h3&gt;
&lt;p&gt;After trying out bubblewrap and convincing myself it does actually work, I
installed claude code&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_c22e562bdb0048bf8a23da9d718e3cf7-1" name="rest_code_c22e562bdb0048bf8a23da9d718e3cf7-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c22e562bdb0048bf8a23da9d718e3cf7-1"&gt;&lt;/a&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-fsSL&lt;span class="w"&gt; &lt;/span&gt;https://claude.ai/install.sh&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bash
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Really Anthropic, this is the best way to install claude? No dpkg?&lt;/p&gt;
&lt;p&gt;I ran claude once (unsafely) to get logged in. It opened a webpage, and saved
the login to the &lt;code class="docutils literal"&gt;oathAccount&lt;/code&gt; field in &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;~/.claude.json&lt;/span&gt;&lt;/code&gt;. Now I changed my
bash script to this to get claude to run inside the bubblewrap sandbox:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-1" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-1"&gt;&lt;/a&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt; 'EOL' &amp;gt;&amp;gt; ./run_claude.sh&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-2" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-2"&gt;&lt;/a&gt;&lt;span class="s"&gt;  claude-safe() {&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-3" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-3" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-3"&gt;&lt;/a&gt;&lt;span class="s"&gt;    bwrap \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-4" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-4" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-4"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /usr /usr \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-5" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-5" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-5"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /etc /etc \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-6" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-6" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-6"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /run /run \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-7" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-7" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-7"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind "$HOME/.local/share/claude" "$HOME/.local/share/claude" \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-8" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-8" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-8"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/lib /lib \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-9" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-9" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-9"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/lib64 /lib64 \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-10" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-10" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-10"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/bin /bin \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-11" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-11" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-11"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink "$HOME/.local/share/claude/versions/2.1.81" "$HOME/.local/bin/claude" \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-12" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-12" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-12"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --proc /proc \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-13" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-13" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-13"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --dev /dev \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-14" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-14" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-14"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --bind $(pwd) $(pwd) \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-15" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-15" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-15"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --bind "$HOME/.claude" "$HOME/.claude" \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-16" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-16" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-16"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --bind "$HOME/.claude.json" "$HOME/.claude.json" \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-17" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-17" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-17"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --chdir $(pwd) \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-18" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-18" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-18"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --unshare-user --unshare-pid --unshare-ipc --unshare-uts --unshare-cgroup \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-19" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-19" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-19"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --die-with-parent \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-20" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-20" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-20"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --hostname bwrap \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-21" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-21" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-21"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --tmpfs /tmp \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-22" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-22" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-22"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --setenv PATH "$HOME/.local/bin:$PATH" \&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-23" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-23" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-23"&gt;&lt;/a&gt;&lt;span class="s"&gt;      claude "$@"&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-24" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-24" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-24"&gt;&lt;/a&gt;&lt;span class="s"&gt;  }&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-25" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-25" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-25"&gt;&lt;/a&gt;&lt;span class="s"&gt;EOL&lt;/span&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-26" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-26" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-26"&gt;&lt;/a&gt;
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-27" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-27" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-27"&gt;&lt;/a&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./run_claude.sh
&lt;a id="rest_code_0bd501ad66b8401c94a09abb0965f1bb-28" name="rest_code_0bd501ad66b8401c94a09abb0965f1bb-28" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_0bd501ad66b8401c94a09abb0965f1bb-28"&gt;&lt;/a&gt;claude-safe
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now I can use claude. Note it needs some more directories in order to run. This
script hard-codes the version, in the future YMMV. I want it to be able to look
at github, and also my local checkout of cpython so it can examine differences.
I created a read-only token by clicking on my avatar in the upper right corner
of a github we page, then going to Settings → Developer settings → Personal
access tokens → Fine-grained tokens → Generate new token. Since pypy is in the
pypy org, I used "Repository owner: pypy", "Repository access: pypy (only)" and
"Permissions: Contents". Then I made doubly sure the token permissions were
read-only. And checked again. Then I copied the token to the bash script. I
also added a &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;ro-bind&lt;/span&gt;&lt;/code&gt; to the cpython checkout, so I could tell claude code
where to look for CPython implementations of missing PyPy functionality.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_47539cea8f774219b23b06f75d181e00-1" name="rest_code_47539cea8f774219b23b06f75d181e00-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_47539cea8f774219b23b06f75d181e00-1"&gt;&lt;/a&gt;--ro-bind&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/oss/cpython"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/oss/cpython"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;a id="rest_code_47539cea8f774219b23b06f75d181e00-2" name="rest_code_47539cea8f774219b23b06f75d181e00-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_47539cea8f774219b23b06f75d181e00-2"&gt;&lt;/a&gt;--setenv&lt;span class="w"&gt; &lt;/span&gt;GH_TOKEN&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hah, sharing my token would not have been smart"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="claude-sandbox"&gt;
&lt;h3&gt;Claude /sandbox&lt;/h3&gt;
&lt;p&gt;Claude comes with its own sandbox, configured by using the &lt;code class="docutils literal"&gt;/sandbox&lt;/code&gt; command.
I chose the defaults, which prevents malicious code in the repo from accessing
the file system and the network. I was missing some packages to get this to
work. Claude would hang until I installed them, and I needed to kill it with
&lt;code class="docutils literal"&gt;kill&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_b1f99dca990e460a90c201cd7ffdd313-1" name="rest_code_b1f99dca990e460a90c201cd7ffdd313-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_b1f99dca990e460a90c201cd7ffdd313-1"&gt;&lt;/a&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;socat
&lt;a id="rest_code_b1f99dca990e460a90c201cd7ffdd313-2" name="rest_code_b1f99dca990e460a90c201cd7ffdd313-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_b1f99dca990e460a90c201cd7ffdd313-2"&gt;&lt;/a&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;npm&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-g&lt;span class="w"&gt; &lt;/span&gt;@anthropic-ai/sandbox-runtime
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="final-touches"&gt;
&lt;h3&gt;Final touches&lt;/h3&gt;
&lt;p&gt;One last thing that I discovered later: I needed to give claude access to some
grepping and git tools. While git should be locked down externally so it
cannot push to the repo, I do want claude to look at other issues and pull
requests in read-only mode. So I added a local &lt;code class="docutils literal"&gt;.claude/settings.json&lt;/code&gt; file
inside the repo (see below for which directory to do this):&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code json"&gt;&lt;a id="rest_code_92d0148fbe964c40bce913ea1b64c188-1" name="rest_code_92d0148fbe964c40bce913ea1b64c188-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_92d0148fbe964c40bce913ea1b64c188-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_92d0148fbe964c40bce913ea1b64c188-2" name="rest_code_92d0148fbe964c40bce913ea1b64c188-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_92d0148fbe964c40bce913ea1b64c188-2"&gt;&lt;/a&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_92d0148fbe964c40bce913ea1b64c188-3" name="rest_code_92d0148fbe964c40bce913ea1b64c188-3" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_92d0148fbe964c40bce913ea1b64c188-3"&gt;&lt;/a&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;a id="rest_code_92d0148fbe964c40bce913ea1b64c188-4" name="rest_code_92d0148fbe964c40bce913ea1b64c188-4" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_92d0148fbe964c40bce913ea1b64c188-4"&gt;&lt;/a&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(sed*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_92d0148fbe964c40bce913ea1b64c188-5" name="rest_code_92d0148fbe964c40bce913ea1b64c188-5" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_92d0148fbe964c40bce913ea1b64c188-5"&gt;&lt;/a&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(grep*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_92d0148fbe964c40bce913ea1b64c188-6" name="rest_code_92d0148fbe964c40bce913ea1b64c188-6" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_92d0148fbe964c40bce913ea1b64c188-6"&gt;&lt;/a&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(cat*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_92d0148fbe964c40bce913ea1b64c188-7" name="rest_code_92d0148fbe964c40bce913ea1b64c188-7" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_92d0148fbe964c40bce913ea1b64c188-7"&gt;&lt;/a&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(find*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_92d0148fbe964c40bce913ea1b64c188-8" name="rest_code_92d0148fbe964c40bce913ea1b64c188-8" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_92d0148fbe964c40bce913ea1b64c188-8"&gt;&lt;/a&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(rg*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_92d0148fbe964c40bce913ea1b64c188-9" name="rest_code_92d0148fbe964c40bce913ea1b64c188-9" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_92d0148fbe964c40bce913ea1b64c188-9"&gt;&lt;/a&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(python*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_92d0148fbe964c40bce913ea1b64c188-10" name="rest_code_92d0148fbe964c40bce913ea1b64c188-10" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_92d0148fbe964c40bce913ea1b64c188-10"&gt;&lt;/a&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(pytest*)"&lt;/span&gt;
&lt;a id="rest_code_92d0148fbe964c40bce913ea1b64c188-11" name="rest_code_92d0148fbe964c40bce913ea1b64c188-11" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_92d0148fbe964c40bce913ea1b64c188-11"&gt;&lt;/a&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a id="rest_code_92d0148fbe964c40bce913ea1b64c188-12" name="rest_code_92d0148fbe964c40bce913ea1b64c188-12" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_92d0148fbe964c40bce913ea1b64c188-12"&gt;&lt;/a&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;a id="rest_code_92d0148fbe964c40bce913ea1b64c188-13" name="rest_code_92d0148fbe964c40bce913ea1b64c188-13" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_92d0148fbe964c40bce913ea1b64c188-13"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then I made git ignore it, even when doing a &lt;code class="docutils literal"&gt;git clean&lt;/code&gt;, in a local (not part
of the repo) configuration&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_664806b3e81f4a8f8e845388ffd01fd6-1" name="rest_code_664806b3e81f4a8f8e845388ffd01fd6-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_664806b3e81f4a8f8e845388ffd01fd6-1"&gt;&lt;/a&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;.claude&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;~/.config/git/ignore
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="what-about-git-push"&gt;
&lt;h3&gt;What about &lt;code class="docutils literal"&gt;git push&lt;/code&gt;?&lt;/h3&gt;
&lt;p&gt;I don't want claude messing around with the upstream repo, only read access. But
I did not actively prevent &lt;code class="docutils literal"&gt;git push&lt;/code&gt;. So instead of using my actual pypy
repo, I cloned it to a separate directory and did not add a remote pointing to
github.com.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="fixing-tests-easy"&gt;
&lt;h2&gt;Fixing tests - easy&lt;/h2&gt;
&lt;p&gt;Now that everything is set up (I hope I remembered everything), I could start
asking questions. The technique I chose was to feed claude the whole test
failure from the buildbot. So starting from the &lt;a class="reference external" href="https://buildbot.pypy.org/summary?branch=py3.11"&gt;buildbot py3.11 summary&lt;/a&gt;,
click on one of the &lt;code class="docutils literal"&gt;F&lt;/code&gt; links and copy-paste all that into the claude prompt.
It didn't take long for claude to come up with solutions for the long-standing
&lt;a class="reference external" href="https://github.com/pypy/pypy/commit/9e8e121b545dbea3f26ca436ae8a797617904306#diff-ab042b3dd16bf22b7e3d8595f182ad39d3823d76b414da7debe96081a884d16bR64-R330"&gt;ctype error missing exception&lt;/a&gt; which turned out to be due to an missing error
trap when already handling an error.&lt;/p&gt;
&lt;p&gt;Also a &lt;a class="reference external" href="https://github.com/pypy/pypy/commit/9e8e121b545dbea3f26ca436ae8a797617904306#diff-ab042b3dd16bf22b7e3d8595f182ad39d3823d76b414da7debe96081a884d16bR64-R53"&gt;CTYPES_MAX_ARGCOUNT check&lt;/a&gt; was
missing. At first, claude wanted to change the ctypes code from CPython's stdlib,
and so I had to make it clear that claude was not to touch the files in
&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;lib-python&lt;/span&gt;&lt;/code&gt;. They are copied verbatim from CPython and should not be
modified without really good reasons.&lt;/p&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://github.com/pypy/pypy/commit/39ca7a1def272742e8aafd2a649ed4f8fed7038d"&gt;fix to raise&lt;/a&gt; &lt;code class="docutils literal"&gt;TypeError&lt;/code&gt; rather
than &lt;code class="docutils literal"&gt;Attribute Error&lt;/code&gt; for deleting ctype object's &lt;code class="docutils literal"&gt;value&lt;/code&gt; was maybe a little
trickier: claude needed to create its own &lt;code class="docutils literal"&gt;property&lt;/code&gt; class and use it in
assignments.&lt;/p&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://github.com/pypy/pypy/commit/e0e401699c20a92d8db657879183c68ea44246b4"&gt;fix for a failing test&lt;/a&gt; for a correct &lt;code class="docutils literal"&gt;repr&lt;/code&gt; of a ctypes array was a
little more involved.  Claude needed to figure out that &lt;code class="docutils literal"&gt;newmemoryview&lt;/code&gt; was
raising an exception, dive into the RPython implementation and fix the problem,
and then also fix a pure-python &lt;code class="docutils literal"&gt;__buffer__&lt;/code&gt; shape edge case error.&lt;/p&gt;
&lt;p&gt;There were more, but you get the idea. With a little bit of coaching, and by showing
claude where the CPython implementation was, more tests are now passing.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="fixing-tests-harder"&gt;
&lt;h2&gt;Fixing tests - harder&lt;/h2&gt;
&lt;p&gt;PyPy has a HPy backend. There were some test failures that were
easy to fix (a handle not being closed, an annotation warning). But the big one
was a problem with the context tracking before and after ffi function calls. In
debug mode there is a check that the ffi call is done using the correct HPy
context. It turns out to be tricky to hang on to a reference to a context in
RPython since the context RPython object is pre-built. The solution, which took
quite a few tokens and translation cycles to work out, was to assign the
context on the C level, and have a getter to fish it out in RPython.&lt;/p&gt;
&lt;section id="conclusion"&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;I started this journey not more than 24 hours ago, after some successful
sessions using claude to refactor some web sites off hosting platforms and make
them static pages. I was impressed enough to try coding with it from the
terminal. It helps that I was given a generous budget to use Anthropic's tool.&lt;/p&gt;
&lt;p&gt;Claude seems capable of understanding the layers of PyPy: from the pure python
stdlib to RPython and into the small amount of C code. I even asked it to
examine a &lt;a class="reference external" href="https://github.com/pypy/pypy/issues/5398"&gt;segfault&lt;/a&gt; in the recently released PyPy7.3.21, and it seems to have
found the general area where there was a latent bug in the JIT.&lt;/p&gt;
&lt;p&gt;Like any tool, agentic programming must be used carefully to make sure it
cannot do damage. I hope I closed the most obvious foot-guns, if you have other
ideas of things I should do to protect myself while using an agent like this, I
would love to hear about them.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;</description><category>AI</category><guid>https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html</guid><pubDate>Mon, 23 Mar 2026 10:27:55 GMT</pubDate></item></channel></rss>