<?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, 30 Sep 2026 20:34:16 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_427b01cf26a0498ebcaeceaaff7bf0a9-1" name="rest_code_427b01cf26a0498ebcaeceaaff7bf0a9-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_427b01cf26a0498ebcaeceaaff7bf0a9-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_4ce303938cf34bd4a588d394ac7f1c4f-1" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-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_4ce303938cf34bd4a588d394ac7f1c4f-2" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-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_4ce303938cf34bd4a588d394ac7f1c4f-3" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-3" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-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_4ce303938cf34bd4a588d394ac7f1c4f-4" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-4" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-4"&gt;&lt;/a&gt;
&lt;a id="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-5" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-5" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-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_4ce303938cf34bd4a588d394ac7f1c4f-6" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-6" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-6"&gt;&lt;/a&gt;&lt;span class="s1"&gt;  userns,&lt;/span&gt;
&lt;a id="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-7" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-7" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-7"&gt;&lt;/a&gt;&lt;span class="s1"&gt;}&lt;/span&gt;
&lt;a id="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-8" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-8" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-8"&gt;&lt;/a&gt;&lt;span class="s1"&gt;EOF'&lt;/span&gt;
&lt;a id="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-9" name="rest_code_4ce303938cf34bd4a588d394ac7f1c4f-9" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_4ce303938cf34bd4a588d394ac7f1c4f-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_c2d6ba816a794bc39d2595be2a57c4f1-1" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-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_c2d6ba816a794bc39d2595be2a57c4f1-2" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-2"&gt;&lt;/a&gt;&lt;span class="s"&gt;  function call_bwrap() {&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-3" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-3" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-3"&gt;&lt;/a&gt;&lt;span class="s"&gt;    bwrap \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-4" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-4" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-4"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /usr /usr \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-5" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-5" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-5"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /etc /etc \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-6" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-6" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-6"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /run /run \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-7" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-7" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-7"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/lib /lib \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-8" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-8" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-8"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/lib64 /lib64 \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-9" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-9" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-9"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/bin /bin \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-10" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-10" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-10"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --proc /proc \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-11" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-11" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-11"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --dev /dev \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-12" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-12" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-12"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --bind $(pwd) $(pwd) \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-13" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-13" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-13"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --chdir $(pwd) \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-14" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-14" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-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_c2d6ba816a794bc39d2595be2a57c4f1-15" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-15" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-15"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --die-with-parent \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-16" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-16" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-16"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --hostname bwrap \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-17" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-17" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-17"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --tmpfs /tmp \&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-18" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-18" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-18"&gt;&lt;/a&gt;&lt;span class="s"&gt;      /bin/bash "$@"&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-19" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-19" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-19"&gt;&lt;/a&gt;&lt;span class="s"&gt;  }&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-20" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-20" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-20"&gt;&lt;/a&gt;&lt;span class="s"&gt;EOL&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-21" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-21" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-21"&gt;&lt;/a&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-22" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-22" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-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_c2d6ba816a794bc39d2595be2a57c4f1-23" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-23" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-23"&gt;&lt;/a&gt;call_bwrap
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-24" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-24" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-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_c2d6ba816a794bc39d2595be2a57c4f1-25" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-25" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-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_c2d6ba816a794bc39d2595be2a57c4f1-26" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-26" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-26"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# the sandbox&lt;/span&gt;
&lt;a id="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-27" name="rest_code_c2d6ba816a794bc39d2595be2a57c4f1-27" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_c2d6ba816a794bc39d2595be2a57c4f1-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_a483295e055347bd8175832b9b307666-1" name="rest_code_a483295e055347bd8175832b9b307666-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a483295e055347bd8175832b9b307666-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_a7413cf39e9c456ba119b9a0a6bdf1e4-1" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-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_a7413cf39e9c456ba119b9a0a6bdf1e4-2" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-2"&gt;&lt;/a&gt;&lt;span class="s"&gt;  claude-safe() {&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-3" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-3" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-3"&gt;&lt;/a&gt;&lt;span class="s"&gt;    bwrap \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-4" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-4" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-4"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /usr /usr \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-5" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-5" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-5"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /etc /etc \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-6" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-6" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-6"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --ro-bind /run /run \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-7" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-7" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-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_a7413cf39e9c456ba119b9a0a6bdf1e4-8" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-8" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-8"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/lib /lib \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-9" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-9" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-9"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/lib64 /lib64 \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-10" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-10" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-10"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --symlink usr/bin /bin \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-11" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-11" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-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_a7413cf39e9c456ba119b9a0a6bdf1e4-12" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-12" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-12"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --proc /proc \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-13" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-13" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-13"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --dev /dev \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-14" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-14" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-14"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --bind $(pwd) $(pwd) \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-15" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-15" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-15"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --bind "$HOME/.claude" "$HOME/.claude" \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-16" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-16" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-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_a7413cf39e9c456ba119b9a0a6bdf1e4-17" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-17" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-17"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --chdir $(pwd) \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-18" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-18" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-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_a7413cf39e9c456ba119b9a0a6bdf1e4-19" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-19" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-19"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --die-with-parent \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-20" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-20" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-20"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --hostname bwrap \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-21" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-21" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-21"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --tmpfs /tmp \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-22" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-22" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-22"&gt;&lt;/a&gt;&lt;span class="s"&gt;      --setenv PATH "$HOME/.local/bin:$PATH" \&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-23" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-23" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-23"&gt;&lt;/a&gt;&lt;span class="s"&gt;      claude "$@"&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-24" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-24" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-24"&gt;&lt;/a&gt;&lt;span class="s"&gt;  }&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-25" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-25" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-25"&gt;&lt;/a&gt;&lt;span class="s"&gt;EOL&lt;/span&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-26" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-26" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-26"&gt;&lt;/a&gt;
&lt;a id="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-27" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-27" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-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_a7413cf39e9c456ba119b9a0a6bdf1e4-28" name="rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-28" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_a7413cf39e9c456ba119b9a0a6bdf1e4-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_912b59cd29f34fa4b72a4fef84a34ab8-1" name="rest_code_912b59cd29f34fa4b72a4fef84a34ab8-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_912b59cd29f34fa4b72a4fef84a34ab8-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_912b59cd29f34fa4b72a4fef84a34ab8-2" name="rest_code_912b59cd29f34fa4b72a4fef84a34ab8-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_912b59cd29f34fa4b72a4fef84a34ab8-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_afeda2dc07b949c5bf946c10aa09900a-1" name="rest_code_afeda2dc07b949c5bf946c10aa09900a-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_afeda2dc07b949c5bf946c10aa09900a-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_afeda2dc07b949c5bf946c10aa09900a-2" name="rest_code_afeda2dc07b949c5bf946c10aa09900a-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_afeda2dc07b949c5bf946c10aa09900a-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_88fa74149a2047bdb223a5cb834c8973-1" name="rest_code_88fa74149a2047bdb223a5cb834c8973-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_88fa74149a2047bdb223a5cb834c8973-2" name="rest_code_88fa74149a2047bdb223a5cb834c8973-2" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-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_88fa74149a2047bdb223a5cb834c8973-3" name="rest_code_88fa74149a2047bdb223a5cb834c8973-3" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-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_88fa74149a2047bdb223a5cb834c8973-4" name="rest_code_88fa74149a2047bdb223a5cb834c8973-4" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-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_88fa74149a2047bdb223a5cb834c8973-5" name="rest_code_88fa74149a2047bdb223a5cb834c8973-5" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-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_88fa74149a2047bdb223a5cb834c8973-6" name="rest_code_88fa74149a2047bdb223a5cb834c8973-6" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-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_88fa74149a2047bdb223a5cb834c8973-7" name="rest_code_88fa74149a2047bdb223a5cb834c8973-7" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-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_88fa74149a2047bdb223a5cb834c8973-8" name="rest_code_88fa74149a2047bdb223a5cb834c8973-8" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-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_88fa74149a2047bdb223a5cb834c8973-9" name="rest_code_88fa74149a2047bdb223a5cb834c8973-9" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-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_88fa74149a2047bdb223a5cb834c8973-10" name="rest_code_88fa74149a2047bdb223a5cb834c8973-10" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-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_88fa74149a2047bdb223a5cb834c8973-11" name="rest_code_88fa74149a2047bdb223a5cb834c8973-11" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-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_88fa74149a2047bdb223a5cb834c8973-12" name="rest_code_88fa74149a2047bdb223a5cb834c8973-12" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-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_88fa74149a2047bdb223a5cb834c8973-13" name="rest_code_88fa74149a2047bdb223a5cb834c8973-13" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_88fa74149a2047bdb223a5cb834c8973-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_5349ae38b1c743a49bb36c9b966a5932-1" name="rest_code_5349ae38b1c743a49bb36c9b966a5932-1" href="https://www.pypy.org/posts/2026/03/using-claude-to-fix-pypy311-test-failures-securely.html#rest_code_5349ae38b1c743a49bb36c9b966a5932-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>