<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bargan Constantin</title>
    <description>The latest articles on DEV Community by Bargan Constantin (@bargan).</description>
    <link>https://dev.to/bargan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3543196%2F9791d07a-3a9b-4467-89ac-f71870243f93.png</url>
      <title>DEV Community: Bargan Constantin</title>
      <link>https://dev.to/bargan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bargan"/>
    <language>en</language>
    <item>
      <title>Is it your agents? Reading load against your core count, per-core CPU, memory and temperature</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Sun, 01 Nov 2026 20:17:02 +0000</pubDate>
      <link>https://dev.to/bargan/is-it-your-agents-reading-load-against-your-core-count-per-core-cpu-memory-and-temperature-4oek</link>
      <guid>https://dev.to/bargan/is-it-your-agents-reading-load-against-your-core-count-per-core-cpu-memory-and-temperature-4oek</guid>
      <description>&lt;p&gt;This article was prepared with AI assistance, using ccdeck’s source code and documentation to check the technical details.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I build &lt;a href="https://github.com/BarganConstantin/ccdeck" rel="noopener noreferrer"&gt;ccdeck&lt;/a&gt;, a local dashboard for Claude Code and Codex sessions. Its This machine panel is the worked example here. The Linux facts come from the linked man pages and kernel documentation. The panel examples were checked against 3.48.0 and rechecked against the public README on 1 November 2026. All pictured readings, including the cover, are demo data rather than measurements of a real workload.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The fans spin up and the laptop gets hot under your wrists. Nothing &lt;em&gt;you&lt;/em&gt; started is heavy. But four agents are running, and in the last ten minutes each of them decided the right next step was to run the test suite, a type check and a bundler, all at once.&lt;/p&gt;

&lt;p&gt;The questions are old ones: is the machine actually overloaded, by what, and how hot is it? The numbers that answer them are easy to misread. Here is how to read each one, with ccdeck's panel as an example of putting them next to the sessions that cause them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Load average is a queue, not a percentage
&lt;/h2&gt;

&lt;p&gt;The first three fields of &lt;code&gt;/proc/loadavg&lt;/code&gt; count runnable tasks and tasks in uninterruptible I/O waits, averaged over 1, 5 and 15 minutes (&lt;a href="https://man7.org/linux/man-pages/man5/proc_loadavg.5.html" rel="noopener noreferrer"&gt;proc_loadavg(5)&lt;/a&gt;). The figures are not divided by CPU count: the same load can mean very different things on a one-core and a sixteen-core machine.&lt;/p&gt;

&lt;p&gt;So the number only means something next to your core count:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s1"&gt;'{ printf "1-minute load %.2f on %d cores: %.2fx\n", $1, n, $1/n }'&lt;/span&gt; /proc/loadavg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above &lt;code&gt;1.00x&lt;/code&gt;, the average count of running or waiting tasks exceeds the number of available logical CPUs. That is a reason to investigate, not a diagnosis by itself. Two cautions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;On Linux, the queue includes tasks waiting for disk&lt;/strong&gt; (state D). A high load with idle CPUs points at I/O; &lt;code&gt;top&lt;/code&gt;'s &lt;code&gt;wa&lt;/code&gt; field is "time waiting for I/O completion".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows publishes no load average&lt;/strong&gt;, so there is no such figure to read there.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ccdeck's panel does this division for you: when the one-minute load is above your core count, a line says by how much, as in &lt;em&gt;1.2× more work queued than cores to run it&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq98hucm4ups7vpocm6ju.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq98hucm4ups7vpocm6ju.webp" alt="The This machine panel: 16 cores at 78% CPU, load average 18.62 with 1.2× more work queued than cores to run it, 21.6 of 32 GB physical memory, download and upload rates, Claude API at 38 ms, a GPU section for an NVIDIA GeForce RTX 4070, then Thermal with CPU 71 °C and GPU 54 °C" width="340" height="726"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data: a 16-core Linux computer while the demo sessions run their tests.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  One pinned core hides in the total
&lt;/h2&gt;

&lt;p&gt;On a 16-core machine, one single-threaded build at 100% is 6.25% of the whole. The total looks calm while that build is as fast as it will ever be.&lt;/p&gt;

&lt;p&gt;Look per core instead. In &lt;code&gt;top&lt;/code&gt;, the &lt;code&gt;1&lt;/code&gt; key is the "Single/Separate-Cpu-States toggle"; &lt;code&gt;mpstat -P ALL 1&lt;/code&gt; (from sysstat) prints the same every second. ccdeck's panel draws one column per logical core under the total for this reason, and never colours CPU as a warning: at 90%, the computer is doing the work you asked for.&lt;/p&gt;


&lt;h2&gt;
  
  
  A process's %CPU is per core, and &lt;code&gt;ps&lt;/code&gt; averages over its lifetime
&lt;/h2&gt;

&lt;p&gt;Two tools, two meanings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://man7.org/linux/man-pages/man1/top.1.html" rel="noopener noreferrer"&gt;&lt;code&gt;top&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;: a multithreaded process can exceed 100% in Irix mode. Solaris mode divides the figure by the number of CPUs; &lt;code&gt;I&lt;/code&gt; switches between them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://man7.org/linux/man-pages/man1/ps.1.html" rel="noopener noreferrer"&gt;&lt;code&gt;ps&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;: &lt;code&gt;%cpu&lt;/code&gt; averages CPU time over the process's elapsed lifetime. A test runner that has been busy for ten seconds and idle for an hour looks quiet in &lt;code&gt;ps&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So 312 in a per-core column is a little over three cores, and a &lt;code&gt;ps&lt;/code&gt; figure is history, not now. ccdeck's &lt;strong&gt;Busiest processes&lt;/strong&gt; list uses the per-core convention: the 40 busiest processes by CPU and the 40 largest by memory, with the resident size, the figure &lt;code&gt;ps&lt;/code&gt; and Task Manager show.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl9hb59y185pkrs8c9awf.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl9hb59y185pkrs8c9awf.webp" alt="Busiest processes, sorted by CPU: node running vitest at 312, esbuild at 188, node running tsc at 97, rg, claude, postgres, codex" width="800" height="437"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data: one session's tests, bundler and type check are the three busiest.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding the session behind a process&lt;/strong&gt; is the step no single number gives you. On Linux, the process tree does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ps &lt;span class="nt"&gt;-eo&lt;/span&gt; pid,ppid,pcpu,rss,args &lt;span class="nt"&gt;--forest&lt;/span&gt; | less &lt;span class="nt"&gt;-S&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--forest&lt;/code&gt; draws the tree ("ASCII art process tree"), so a &lt;code&gt;vitest&lt;/code&gt; shows up under the &lt;code&gt;claude&lt;/code&gt; or &lt;code&gt;codex&lt;/code&gt; process that started it, and &lt;code&gt;pwdx &amp;lt;pid&amp;gt;&lt;/code&gt; prints its working folder, which you can match to a session. ccdeck's list does not make that link for you; you see &lt;code&gt;vitest&lt;/code&gt; at 312 and a session running tests, and connecting the two is still yours. It also cannot end a process; that belongs to a terminal, Activity Monitor or Task Manager.&lt;/p&gt;




&lt;h2&gt;
  
  
  Memory: trust "available", not "used"
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://man7.org/linux/man-pages/man5/proc_meminfo.5.html" rel="noopener noreferrer"&gt;&lt;code&gt;/proc/meminfo&lt;/code&gt;&lt;/a&gt; has a field made for the question "can I start more work?": &lt;code&gt;MemAvailable&lt;/code&gt;, an estimate of memory available to new applications without swapping. On Linux, total minus available gives a pressure-oriented view of physical memory in use. The example panel shows this as &lt;em&gt;Physical&lt;/em&gt; and turns its bar amber from 90%. It shows swap separately; on Windows that second row is &lt;em&gt;Commit&lt;/em&gt;, a different measure from physical RAM.&lt;/p&gt;




&lt;h2&gt;
  
  
  GPUs: ask the driver
&lt;/h2&gt;

&lt;p&gt;An NVIDIA card's own figures come from its driver's &lt;code&gt;nvidia-smi&lt;/code&gt;, on Linux and Windows. ccdeck's panel asks it every 5 seconds for each GPU's utilization, video memory used and total, temperature and power, and draws a section per GPU; with no &lt;code&gt;nvidia-smi&lt;/code&gt; on the PATH it draws none and installs nothing. It keeps no history of these, and they are the GPUs in your computer, not the ones running a cloud model.&lt;/p&gt;




&lt;h2&gt;
  
  
  Temperature: where each system keeps it, or does not
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Linux: sysfs, in millidegrees
&lt;/h3&gt;

&lt;p&gt;Two places, both readable without root:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;hwmon&lt;/strong&gt;: each &lt;code&gt;/sys/class/hwmon/hwmonX&lt;/code&gt; has a &lt;code&gt;name&lt;/code&gt; (such as &lt;code&gt;coretemp&lt;/code&gt; or &lt;code&gt;nvme&lt;/code&gt;) and &lt;code&gt;tempY_input&lt;/code&gt; files, in "millidegree Celsius" (&lt;a href="https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-hwmon" rel="noopener noreferrer"&gt;kernel ABI&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;thermal zones&lt;/strong&gt;: &lt;code&gt;/sys/class/thermal/thermal_zoneX/temp&lt;/code&gt;, also in millidegrees, with a &lt;code&gt;type&lt;/code&gt; such as &lt;code&gt;acpitz&lt;/code&gt;, "an ACPI thermal device" (&lt;a href="https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-thermal" rel="noopener noreferrer"&gt;kernel ABI&lt;/a&gt;).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;h &lt;span class="k"&gt;in&lt;/span&gt; /sys/class/hwmon/hwmon&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%-12s %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$h&lt;/span&gt;&lt;span class="s2"&gt;/name"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$h&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/temp&lt;span class="k"&gt;*&lt;/span&gt;_input 2&amp;gt;/dev/null | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Intel, the kernel also reports thermal throttling per CPU under &lt;code&gt;/sys/devices/system/cpu/cpuX/thermal_throttle/&lt;/code&gt;: event counts and cumulative active time, including &lt;code&gt;core_throttle_total_time_ms&lt;/code&gt; and its package counterpart (&lt;a href="https://docs.kernel.org/admin-guide/thermal/intel_thermal_throttle.html" rel="noopener noreferrer"&gt;kernel docs&lt;/a&gt;). Sample an active-time counter twice and divide its increase by the elapsed time to estimate the fraction of that interval spent throttling. ccdeck reads hwmon first, then thermal zones; its Linux throttling reading uses changes in &lt;code&gt;package_throttle_total_time_ms&lt;/code&gt;, not the event count alone.&lt;/p&gt;

&lt;h3&gt;
  
  
  macOS: readable on Intel, private on Apple silicon
&lt;/h3&gt;

&lt;p&gt;On an Intel Mac, &lt;code&gt;ioreg&lt;/code&gt; gives the GPU and &lt;code&gt;pmset&lt;/code&gt; says how much of the CPU's speed is held back to cool it. On an M-series Mac, no command that ships with macOS prints a temperature: &lt;code&gt;powermetrics&lt;/code&gt; needs root, &lt;code&gt;pmset -g therm&lt;/code&gt; records nothing there, and the sensors sit behind a private API only native code can call. &lt;a href="https://github.com/vladkens/macmon" rel="noopener noreferrer"&gt;macmon&lt;/a&gt; reads them without &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;ccdeck does not ask you to install it. When the Mac's own sensors give nothing, it downloads macmon's published binary from GitHub once, verifies it against the SHA-256 the release publishes, checks that it runs, and keeps it in &lt;code&gt;~/.agents-deck/tools/macmon&lt;/code&gt;. That is skipped if &lt;code&gt;macmon&lt;/code&gt; is already on your PATH or from Homebrew, on an Intel Mac, and under &lt;code&gt;AGENTS_DECK_NO_DOWNLOAD=1&lt;/code&gt; or &lt;code&gt;AGENTS_DECK_NO_INSTALL=1&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Windows: often nothing, and that is the honest answer
&lt;/h3&gt;

&lt;p&gt;The places an ordinary program can look are a performance counter, &lt;code&gt;MSAcpi_ThermalZoneTemperature&lt;/code&gt;, and LibreHardwareMonitor's web server if you happen to run it. On many Windows laptops, all of them come up empty. ccdeck's README documents one such machine, a Windows 11 laptop with an Intel i5-9300H, checked as a normal user and as an administrator:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Its firmware declares &lt;strong&gt;zero&lt;/strong&gt; ACPI thermal zones, so the performance counter exists but has no instances.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MSAcpi_ThermalZoneTemperature&lt;/code&gt; answers &lt;em&gt;Not supported&lt;/em&gt;, even to an administrator.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Win32_TemperatureProbe&lt;/code&gt; exists, but every field reads &lt;code&gt;32768&lt;/code&gt;, WMI's value for "unknown".&lt;/li&gt;
&lt;li&gt;The sensors are real, managed by Intel Dynamic Tuning, which publishes them where only an administrator can read.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is a class of machine, not a fault. There is no standard user-mode Windows API for CPU temperature, which is why HWiNFO, Core Temp and LibreHardwareMonitor all install a kernel driver.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And a thermal zone is not the CPU.&lt;/strong&gt; It measures whatever the firmware wired it to, and on many laptops nothing: Intel's reference firmware then reports a fixed 27.85 °C, so the zone reads 28 °C however hot the CPU is. A figure that never moves is a placeholder. ccdeck draws a zone only once its value has changed, and calls the row &lt;em&gt;Thermal zone&lt;/em&gt;, never &lt;em&gt;CPU&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Virtual machines often expose no temperature sensor. When a sensor is unavailable, displaying &lt;code&gt;0 °C&lt;/code&gt; would confuse a missing value with a measurement; leave the reading unavailable instead. A constant value deserves investigation rather than automatic treatment as a real CPU temperature.&lt;/p&gt;




&lt;h2&gt;
  
  
  Keep a little history, at the right resolution
&lt;/h2&gt;

&lt;p&gt;A spike you did not see is the one you are asking about. ccdeck keeps up to a day of each reading in memory, one point per minute holding &lt;strong&gt;that minute's highest value&lt;/strong&gt;, so a short spike is not averaged away; dashed lines mark where memory, swap and the queue turn amber or red. It is memory only, so it starts again when the deck restarts. If you log your own, keep the maximum per bucket, not the mean.&lt;/p&gt;

&lt;p&gt;The machine readings are local and served on &lt;code&gt;127.0.0.1&lt;/code&gt;. The panel's latency indicator additionally performs a DNS lookup and TCP handshake to &lt;code&gt;api.anthropic.com:443&lt;/code&gt; about every 30 seconds while it is open on a machine with Claude Code; it sends no application payload. An Apple silicon Mac may also need the macmon download described above. These are the machine panel's network operations, not an inventory of the entire application's traffic.&lt;/p&gt;




&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Divide the 1-minute load by &lt;code&gt;nproc&lt;/code&gt;. Above 1, work is queued; on Linux, check &lt;code&gt;wa&lt;/code&gt; before blaming the CPU.&lt;/li&gt;
&lt;li&gt;Look per core. One pinned core is invisible in the total.&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;top&lt;/code&gt;, 312% is about three cores; in &lt;code&gt;ps&lt;/code&gt;, %CPU is a lifetime average.&lt;/li&gt;
&lt;li&gt;Trust &lt;code&gt;MemAvailable&lt;/code&gt;, not "used".&lt;/li&gt;
&lt;li&gt;Temperatures live in sysfs on Linux, behind a private API on Apple silicon, and often nowhere an ordinary program can reach on Windows laptops. No reading is a valid answer; a fixed 28 °C is not a reading.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linux</category>
      <category>performance</category>
      <category>claudecode</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to write a Claude Code hook that cannot steer your agent</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Thu, 29 Oct 2026 16:33:52 +0000</pubDate>
      <link>https://dev.to/bargan/how-to-write-a-claude-code-hook-that-cannot-steer-your-agent-3f83</link>
      <guid>https://dev.to/bargan/how-to-write-a-claude-code-hook-that-cannot-steer-your-agent-3f83</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;I build &lt;a href="https://github.com/BarganConstantin/ccdeck" rel="noopener noreferrer"&gt;ccdeck&lt;/a&gt;, a local dashboard for Claude Code and Codex sessions, and its hook is the worked example; everything about it is quoted from the source at &lt;a href="https://github.com/BarganConstantin/ccdeck/tree/v3.42.0" rel="noopener noreferrer"&gt;the v3.42.0 tag&lt;/a&gt; (&lt;code&gt;hook/hook.js&lt;/code&gt;, &lt;code&gt;src/server/installer.mjs&lt;/code&gt; and &lt;code&gt;src/web/__tests__/hook-read-only.test.ts&lt;/code&gt;, with two more tests named where they come up); at 3.48.0 the hook, its tests and the quoted installer lines are unchanged. Claude Code's behaviour is quoted from Anthropic's &lt;a href="https://code.claude.com/docs/en/hooks" rel="noopener noreferrer"&gt;hooks reference&lt;/a&gt; as I read it on 14 October 2026.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Plenty of hooks only want to watch: a logger, a notifier, a dashboard. They still run on every event they register for, and on some events Claude Code waits for them and reads what they say. A &lt;code&gt;PreToolUse&lt;/code&gt; hook sees &lt;code&gt;rm -rf&lt;/code&gt; before it runs, and its answer can decide whether it does.&lt;/p&gt;

&lt;p&gt;The read-only claim needs to hold in the code. List every channel the hook has for talking back, close each one, and test that it stays closed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Every way a hook can talk back
&lt;/h2&gt;

&lt;p&gt;A command hook gets the event as JSON on stdin. From the reference, it has these ways to affect what happens next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JSON on stdout.&lt;/strong&gt; "Claude Code parses your stdout as JSON output when the whole output is one JSON object", and its fields can allow, deny, defer or rewrite the call. With a valid JSON object, an exit code other than 0 or 2 is ignored: "the fields alone decide".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exit code 2.&lt;/strong&gt; "Exit with code 2 to block the action", with stderr as the reason.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Any other non-zero exit.&lt;/strong&gt; Not a block, but "a &lt;code&gt;&amp;lt;hook name&amp;gt; hook error&lt;/code&gt; notice in the transcript", on every call it happens on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plain text on stdout, on some events.&lt;/strong&gt; For &lt;code&gt;UserPromptSubmit&lt;/code&gt;, &lt;code&gt;UserPromptExpansion&lt;/code&gt;, &lt;code&gt;SessionStart&lt;/code&gt; and &lt;code&gt;PostModelSwitch&lt;/code&gt;, "Claude Code adds plain-text stdout as context that Claude can see and act on". A stray log line there is a message to the model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The entry's own fields.&lt;/strong&gt; &lt;code&gt;onFailure: "block"&lt;/code&gt; turns any failure (a crash, a missing script, a timeout) into a block. &lt;code&gt;async: true&lt;/code&gt; removes the blocking, but not the voice: when an async hook exits, its &lt;code&gt;additionalContext&lt;/code&gt; and &lt;code&gt;systemMessage&lt;/code&gt; reach Claude on the next turn.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time.&lt;/strong&gt; The default timeout for a command hook is 600 seconds on most events, and hooks "block Claude's execution until they complete". A timed-out hook "doesn't block the tool call", but you waited for it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An HTTP hook (&lt;code&gt;type: "http"&lt;/code&gt;) has the same powers through its response body, so a hook that POSTs straight to a local service hands the decision to whatever answers on that port; and a failed connection is a non-blocking error, a notice on every call while the service is down.&lt;/p&gt;

&lt;p&gt;For an observer, that list turns into rules: &lt;strong&gt;a command hook that never writes to stdout, exits 0 on every path, declares no &lt;code&gt;onFailure&lt;/code&gt; or &lt;code&gt;async&lt;/code&gt;, keeps a short timeout, and checks who it is talking to before it sends anything.&lt;/strong&gt; The rest of this post is how ccdeck's hook does each of those, and how its tests pin them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 1: nothing on stdout, exit 0 everywhere
&lt;/h2&gt;

&lt;p&gt;ccdeck's test file opens with the reason it exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The README says the deck cannot steer your agent. This is the file that makes&lt;/span&gt;
&lt;span class="c1"&gt;// that a property of the code rather than an intention.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and names the failure it guards against:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One console.log left in from debugging, or one &lt;code&gt;process.exit(1)&lt;/code&gt; on an error path, turns a read-only observer into a participant in every tool call on the machine — and silently, because a hook that prints unparseable text is ignored until the day it prints something parseable, and a hook that exits non-zero either blocks the call it was reporting (exit 2) or shows a hook error on it (any other code).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every path in &lt;code&gt;hook.js&lt;/code&gt; is written to end at exit 0. A throw is the one way out that a reader of the code cannot see, so there is a last resort:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uncaughtException&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unhandledRejection&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That line came from a bug: a malformed record in the deck's registry crashed the hook with exit 1 and a stack trace, and, per the comment, "Claude Code surfaces a non-zero exit as &lt;code&gt;&amp;lt;hook&amp;gt; hook error&lt;/code&gt; plus the first stderr line", on every tool call.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 2: test the source for every other channel
&lt;/h2&gt;

&lt;p&gt;Writing the rule down is not enough; the next debugging session undoes it. &lt;code&gt;hook-read-only.test.ts&lt;/code&gt; asserts it twice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Over the source&lt;/strong&gt;, it bans every spelling of stdout and every exit code but one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SRC&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;process&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\.\s&lt;/span&gt;&lt;span class="sr"&gt;*stdout&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SRC&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;console&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SRC&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;writeSync&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// …&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;writes&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;req.write(body);&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;          &lt;span class="c1"&gt;// the one write: the outbound request&lt;/span&gt;
&lt;span class="c1"&gt;// …&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;codes&lt;/span&gt;&lt;span class="p"&gt;)]).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;            &lt;span class="c1"&gt;// every process.exit(…) is exit(0)&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SRC&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/process&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\.\s&lt;/span&gt;&lt;span class="sr"&gt;*exitCode/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;console.log&lt;/code&gt; is stdout with another name and &lt;code&gt;fs.writeSync(1, …)&lt;/code&gt; is stdout with no name, so all three spellings are banned. &lt;code&gt;process.exitCode&lt;/code&gt; is banned outright rather than checked for a value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;By running the real script&lt;/strong&gt;, it starts an "honest deck" (a local HTTP server that answers the handshake below), feeds the hook a &lt;code&gt;PreToolUse&lt;/code&gt; for &lt;code&gt;rm -rf /&lt;/code&gt; on stdin, and checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/event&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;   &lt;span class="c1"&gt;// it really delivered&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                 &lt;span class="c1"&gt;// and said nothing back&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The delivery is asserted on purpose: silence from a hook that did nothing would prove nothing. The same silence is then checked on every early return (no deck, unparseable payload, no &lt;code&gt;cwd&lt;/code&gt;), on payloads that throw (a &lt;code&gt;cwd&lt;/code&gt; that is a number, JSON nested 20,000 levels deep), and on eight malformed registry records, and a healthy deck registered next to a malformed record must still receive its event. Every run points &lt;code&gt;CLAUDE_CONFIG_DIR&lt;/code&gt; and &lt;code&gt;HOME&lt;/code&gt; at a temp directory, so the test never reads your real &lt;code&gt;~/.claude&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 3: a time budget counted from process start
&lt;/h2&gt;

&lt;p&gt;On &lt;code&gt;PreToolUse&lt;/code&gt; the user waits until every matching hook returns, and the installer's comment is blunt about the declared &lt;code&gt;timeout&lt;/code&gt;: it "is in SECONDS and it is a kill". So the hook has a deadline per phase and a cap over all of them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CHALLENGE_TIMEOUT_MS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// one retry, only on a timeout&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;POST_TIMEOUT_MS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DISCOVERY_TIMEOUT_MS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// a second window only if the first found nothing&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CAP_MS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1900&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cap is measured from when Node started, not from when &lt;code&gt;main()&lt;/code&gt; ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CAP_MS&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uptime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That detail came from a measurement. The old timer started inside &lt;code&gt;main()&lt;/code&gt;, so the shell fork, Node's start-up and reading a stdin payload that can be megabytes were outside the budget. With a stalled deck, the hook came out at 1.84 s idle and 1.98–2.19 s on a loaded machine, past the &lt;code&gt;timeout: 2&lt;/code&gt; it declared at the time (issue #1018). Now the cap counts from process start, the declared timeout is 3 seconds as a backstop, and &lt;code&gt;hook-budget.test.ts&lt;/code&gt; pins the two numbers against each other so they cannot drift apart again.&lt;/p&gt;

&lt;p&gt;The deadlines can add up to more than the cap (2.6 s if the filesystem, a listener and a deck all stall on one run), and the source says which number to believe: the cap "is the backstop over all of them, and it is what a reader should believe rather than the addition". Discovery gets its second 400 ms window only when the first found no deck and reads were still out, because on a loaded machine those reads are usually slow rather than stalled (issue #1872). A dead port costs nothing: a refused loopback connection is instant.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 4: prove the listener before you send
&lt;/h2&gt;

&lt;p&gt;An observer usually forwards the event somewhere, and the event is everything: in the hook's own words, "prompt text, tool inputs, tool results, cwd". Sending it to a port because a process ID looks alive is not safe. A deck killed with &lt;code&gt;SIGKILL&lt;/code&gt; or a power cut leaves its record behind, the OS can hand the PID to another process, and the port "may by then belong to anything at all (4317, the deck's own default, is also the standard OTLP collector port)".&lt;/p&gt;

&lt;p&gt;So each running deck writes a fresh random token into its record at start-up, and before posting, the hook challenges the listener with a nonce it has never seen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;challengeProof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The token itself never crosses the wire, so a wrong listener learns nothing it could replay, and the answer is compared with &lt;code&gt;crypto.timingSafeEqual&lt;/code&gt;, "purely so a hostile listener cannot walk the expected proof out of us one byte at a time".&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 5: plan for the runtime disappearing
&lt;/h2&gt;

&lt;p&gt;The command written into someone's settings outlives the environment it was written in. ccdeck builds its entries for &lt;code&gt;~/.claude/settings.json&lt;/code&gt; like this, one per event for up to twelve events, tagged so they can be found and removed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildHookEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;MARK_KEY&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                       &lt;span class="c1"&gt;// "__agent-dag"&lt;/span&gt;
    &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command used to name the Node binary directly. Then &lt;code&gt;nvm uninstall&lt;/code&gt; took that binary away, and every tool call of every session showed a hook error from then on. On macOS and Linux the arguments now go through a fixed &lt;code&gt;/bin/sh&lt;/code&gt; script first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NODE_FALLBACK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;n=$1; shift; [ -x "$n" ] || n=$(command -v node) || exit 0; exec "$n" "$@"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It runs the recorded Node while it is still executable, the &lt;code&gt;node&lt;/code&gt; on Claude Code's own PATH once it is not, and nothing at all when there is no Node: exit 0, no output. The Claude Code version is part of the environment too. Until 2.1.101, an event name Claude Code did not know made it ignore the whole settings file, so ccdeck adds its two newest entries only where every Claude Code it finds knows them: &lt;code&gt;StopFailure&lt;/code&gt; from 2.1.78, and &lt;code&gt;UserPromptExpansion&lt;/code&gt; from 2.1.116. That second one is also an event where plain text on stdout becomes context for the model, which is Rule 1 again. The paths reach it as positional arguments, quoted once, so none of them is spliced into shell code. &lt;code&gt;hook-command-node-fallback.test.ts&lt;/code&gt; runs it through a real &lt;code&gt;/bin/sh&lt;/code&gt; and states the trade: "a hook error on every tool call is a worse answer than a deck that hears nothing from a machine it cannot run on."&lt;/p&gt;

&lt;p&gt;One more case of the environment surprising you: &lt;strong&gt;your own tool's Claude runs fire your hooks too.&lt;/strong&gt; ccdeck reads Claude quota in one of its ways with &lt;code&gt;claude --print /usage&lt;/code&gt;, which is itself a Claude Code run. Without a guard, every quota poll drew itself on the dashboard as an empty session. The deck marks that run with &lt;code&gt;AGENTS_DECK_INTERNAL=1&lt;/code&gt;, and the hook exits before reading a byte.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this does not guarantee
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It is a guarantee about one file.&lt;/strong&gt; Anything that can write to your &lt;code&gt;~/.claude&lt;/code&gt; can change &lt;code&gt;settings.json&lt;/code&gt; or the hook itself. No hook design defends against that. (ccdeck writes two other things there, separate from the hook: its Telemetry Radar switches write flags into the &lt;code&gt;env&lt;/code&gt; block when you flip one, and its Settings › Claude Code page writes the one key you change; its uninstall leaves both as you set them.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is a guarantee about the hook, not about every button.&lt;/strong&gt; ccdeck's Continue, on a background session whose turn stopped on an error, runs Claude Code's own &lt;code&gt;claude stop&lt;/code&gt; and &lt;code&gt;claude --resume &amp;lt;id&amp;gt; --bg&lt;/code&gt; at your press. That is a program starting a Claude Code command beside the session, not the hook answering one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The receiver sees everything the hook sees.&lt;/strong&gt; Prompts, tool inputs and results go to &lt;code&gt;127.0.0.1&lt;/code&gt;. That is the point of a dashboard; the handshake exists so they go only to it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fire-and-forget means gaps.&lt;/strong&gt; If no deck is running, the event is not recorded.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Very large events arrive cut.&lt;/strong&gt; The deck refuses a body over 5,000,000 characters, and a &lt;code&gt;PostToolUse&lt;/code&gt; for an Edit carries the whole edited file. Over 4,900,000 the hook replaces the biggest fields with a marker saying how large they were, and flags the event as truncated; the event name, session, tool and call id are never what goes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It still costs a process per event&lt;/strong&gt;: a Node start on every hook call, with a &lt;code&gt;/bin/sh&lt;/code&gt; in front of it on macOS and Linux, bounded by the budget above.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  A checklist for any observing hook
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use a &lt;strong&gt;command&lt;/strong&gt; hook, without &lt;code&gt;onFailure&lt;/code&gt; and without &lt;code&gt;async&lt;/code&gt;. Write &lt;strong&gt;nothing&lt;/strong&gt; to stdout, on any event: on &lt;code&gt;UserPromptSubmit&lt;/code&gt; and &lt;code&gt;SessionStart&lt;/code&gt;, plain text is context for the model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exit 0 on every path&lt;/strong&gt;, and catch the throws you cannot see.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test the source&lt;/strong&gt; for every spelling of stdout and every exit code but 0, then run the real script and assert that it delivered and said nothing.&lt;/li&gt;
&lt;li&gt;Give it a &lt;strong&gt;time budget from process start&lt;/strong&gt;, and keep the declared &lt;code&gt;timeout&lt;/code&gt; strictly above it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prove the listener&lt;/strong&gt; before sending the event; a live PID is not proof.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Survive a missing runtime&lt;/strong&gt; with a silent fallback, and guard against your own tool's runs firing the hook.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;AI assistance was used to prepare this article and check it against the cited source code and documentation.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>node</category>
      <category>security</category>
      <category>testing</category>
    </item>
    <item>
      <title>What Codex CLI writes to disk for every session, and how to follow it live</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Thu, 29 Oct 2026 16:32:55 +0000</pubDate>
      <link>https://dev.to/bargan/what-codex-cli-writes-to-disk-for-every-session-and-how-to-follow-it-live-1i04</link>
      <guid>https://dev.to/bargan/what-codex-cli-writes-to-disk-for-every-session-and-how-to-follow-it-live-1i04</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;I build &lt;a href="https://github.com/BarganConstantin/ccdeck" rel="noopener noreferrer"&gt;ccdeck&lt;/a&gt;, a local dashboard that reads these files, and it is the example in this post; every switch is in &lt;a href="https://ccdeck.dev/guides/codex/" rel="noopener noreferrer"&gt;its Codex guide&lt;/a&gt;. The file samples are from codex-cli 0.162.1 on my machine, Codex's docs are quoted as I read them on 14 October 2026, and ccdeck is described as of 3.48.0.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I run Codex CLI in two or three repositories at once, each in its own terminal, each a scroll of commands, patches and output. Codex has part of the overview itself, and it is worth starting there:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;codex agents&lt;/code&gt;&lt;/strong&gt;, added in Codex CLI 0.149.0, is "an interactive &lt;code&gt;codex agents&lt;/code&gt; dashboard for searching, starting, opening, renaming, and stopping tasks". &lt;code&gt;codex queue&lt;/code&gt;, from the same release, sends messages to existing sessions.&lt;/li&gt;
&lt;li&gt;Inside a session, &lt;strong&gt;&lt;code&gt;/status&lt;/code&gt;&lt;/strong&gt; shows its model, approval policy, writable roots and current token usage, and &lt;strong&gt;&lt;code&gt;/statusline&lt;/code&gt;&lt;/strong&gt; can keep rate limits and token counters in Codex's footer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For anything else, such as every session's tool calls as they happen, or a view that does not depend on which terminal you are in, the source is a file Codex already writes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where the files are
&lt;/h2&gt;

&lt;p&gt;Codex's configuration docs say it "stores its local state under &lt;code&gt;CODEX_HOME&lt;/code&gt; (defaults to &lt;code&gt;~/.codex&lt;/code&gt;)", and name &lt;code&gt;config.toml&lt;/code&gt;, &lt;code&gt;auth.json&lt;/code&gt;, logs and &lt;code&gt;history.jsonl&lt;/code&gt; there. They do not describe the per-session files, which sit beside them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One file per session, in the folder for the day it started. Because the docs leave them out, treat the format as Codex's own: read it defensively, and expect new kinds of line.&lt;/p&gt;




&lt;h2&gt;
  
  
  What a line looks like
&lt;/h2&gt;

&lt;p&gt;Each line is one JSON object with a &lt;code&gt;timestamp&lt;/code&gt;, a &lt;code&gt;type&lt;/code&gt; and a &lt;code&gt;payload&lt;/code&gt;. Counting the kinds in one of my sessions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; ~/.codex/sessions/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/rollout-&lt;span class="k"&gt;*&lt;/span&gt;.jsonl | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'[.type, (.payload.type // "-")] | join(" ")'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    468 event_msg item_completed
    178 event_msg token_count
    176 token_usage_record -
    134 response_item custom_tool_call_output
    134 response_item custom_tool_call
    115 response_item reasoning
     97 response_item message
     28 turn_context -
     27 event_msg task_started
     27 event_msg task_complete
     13 response_item function_call_output
     13 response_item function_call
      1 session_meta -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(trimmed). The ones a live view needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;session_meta&lt;/code&gt;&lt;/strong&gt;, once at the top: the session id, &lt;code&gt;cwd&lt;/code&gt;, &lt;code&gt;cli_version&lt;/code&gt;, &lt;code&gt;model_provider&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;turn_context&lt;/code&gt;&lt;/strong&gt;, once per turn: &lt;code&gt;model&lt;/code&gt;, &lt;code&gt;approval_policy&lt;/code&gt;, &lt;code&gt;sandbox_policy&lt;/code&gt;, &lt;code&gt;effort&lt;/code&gt;. Join mid-session and you know neither the model nor whether the session can stop to ask until the next one arrives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;response_item&lt;/code&gt;&lt;/strong&gt;: the conversation. A tool call is a &lt;code&gt;function_call&lt;/code&gt; or &lt;code&gt;custom_tool_call&lt;/code&gt; line, and its result the matching &lt;code&gt;…_output&lt;/code&gt; line with the same &lt;code&gt;call_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;event_msg&lt;/code&gt;&lt;/strong&gt;: what happened. &lt;code&gt;task_started&lt;/code&gt; and &lt;code&gt;task_complete&lt;/code&gt; bracket a turn; &lt;code&gt;token_count&lt;/code&gt; carries token usage and a rate-limit snapshot.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Following a session live
&lt;/h2&gt;

&lt;p&gt;The file is appended to as the session runs, so following it is &lt;code&gt;tail&lt;/code&gt; plus a parser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 0 &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;--unbuffered&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'[.timestamp, .type, (.payload.type // "")] | @tsv'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A program that does this for every session has three more things to handle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;New files.&lt;/strong&gt; A new session is a new file in today's folder, and a long session, or one picked up with &lt;code&gt;codex resume&lt;/code&gt;, keeps writing to its file in an older day folder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Joining late.&lt;/strong&gt; Start reading mid-session and you have no &lt;code&gt;turn_context&lt;/code&gt; until the next turn: no model, no approval policy, no price for the tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partial lines.&lt;/strong&gt; The last line can be half-written when you read it; keep it until its newline arrives.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ccdeck is one such program. It checks for new lines every 1.5 seconds and turns each session into a card named after its folder:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F75lif991ajoepynafpw0.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F75lif991ajoepynafpw0.webp" alt="A Codex session card: LIVE and a context ring at 15, then a GPT-5.5 chip and its running time on its second row, &amp;quot;approvals not visible&amp;quot;, 3 tools, 1 in-flight, 119.5k tok and 53¢, with three tool bubbles beside it"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data. The last row counts the calls, then gives the tokens and an estimated cost.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The pill reads LIVE while a turn runs and DONE when Codex finishes it, or when you press &lt;code&gt;Esc&lt;/code&gt; in Codex. Tool calls get short names: &lt;code&gt;Shell&lt;/code&gt; for &lt;code&gt;exec&lt;/code&gt;/&lt;code&gt;shell&lt;/code&gt; and friends, &lt;code&gt;Edit&lt;/code&gt; for &lt;code&gt;apply_patch&lt;/code&gt;, &lt;code&gt;Plan&lt;/code&gt; for &lt;code&gt;update_plan&lt;/code&gt;, &lt;code&gt;Web&lt;/code&gt;, &lt;code&gt;stdin&lt;/code&gt; and &lt;code&gt;wait&lt;/code&gt;. A session that was already running when the deck started appears the next time it writes, from that point on, with &lt;code&gt;?&lt;/code&gt; beside its name, &lt;code&gt;≥&lt;/code&gt; before its time and a plain &lt;code&gt;Codex&lt;/code&gt; chip until its next turn starts: point 2 above, made visible. One caught mid-turn, its file written in the last 24 hours, is read back from the end of its file instead and appears at once, marked restored.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure is a guess.&lt;/strong&gt; The file records a tool's output, not a status. ccdeck counts a call as failed only when the output begins "Script failed", or "Exit code:" with a number other than 0, so a command that failed in some other way can still show ✓. Read the output before you trust the tick.&lt;/p&gt;


&lt;h2&gt;
  
  
  Tokens, cost and quota from the same file
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;token_count&lt;/code&gt; events hold two things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usage.&lt;/strong&gt; An &lt;code&gt;info&lt;/code&gt; object with &lt;code&gt;total_token_usage&lt;/code&gt;, &lt;code&gt;last_token_usage&lt;/code&gt; and &lt;code&gt;model_context_window&lt;/code&gt;: input (cached tokens included), output, and the context-window figures from Codex's most recent request. Pricing them needs a rate, and here the file goes only part of the way: it can say which service tier a session asked for (ccdeck reads it from a &lt;code&gt;thread_settings_applied&lt;/code&gt; event), not the tier a request was billed at. ccdeck prices cached tokens at the cache rate, the rest at the tier recorded for them (the standard rate where none is), Codex's auto-review checks on a ChatGPT plan at $0, and labels the result an estimate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A rate-limit snapshot.&lt;/strong&gt; A &lt;code&gt;rate_limits&lt;/code&gt; object with &lt;code&gt;primary&lt;/code&gt; and &lt;code&gt;secondary&lt;/code&gt; windows, each holding &lt;code&gt;used_percent&lt;/code&gt;, &lt;code&gt;window_minutes&lt;/code&gt; and &lt;code&gt;resets_at&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'select(.type == "event_msg" and .payload.type == "token_count")
       | .payload.rate_limits.primary'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"used_percent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;93.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"window_minutes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"resets_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1792383934&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On my account "primary" was the 7-day window, so name windows by &lt;code&gt;window_minutes&lt;/code&gt;, not by position. And it is a snapshot from the last request: a session that has been idle for an hour holds an hour-old reading. ccdeck reads the quota about once a minute while its page is open, from Codex's usage endpoint rather than the file, and names each bar by its length (&lt;strong&gt;5-hour window&lt;/strong&gt;, &lt;strong&gt;7-day window&lt;/strong&gt;, &lt;strong&gt;30-day window&lt;/strong&gt;, or &lt;strong&gt;Rate limit&lt;/strong&gt; when OpenAI gives no length).&lt;/p&gt;




&lt;h2&gt;
  
  
  What the file never says
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When a session is waiting for your approval.&lt;/strong&gt; Codex writes no approval request to the rollout file. A call waiting for you looks exactly like one still running. ccdeck's answer is to say so rather than guess: a running card shows &lt;strong&gt;approvals not visible&lt;/strong&gt; whenever its &lt;code&gt;approval_policy&lt;/code&gt; can ask (&lt;code&gt;untrusted&lt;/code&gt;, &lt;code&gt;on-request&lt;/code&gt;, &lt;code&gt;on-failure&lt;/code&gt;, or not read yet), and nothing at &lt;code&gt;never&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmp3xzd4kr8m4b27wgao4.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmp3xzd4kr8m4b27wgao4.webp" alt="Two Codex cards: infra under on-request shows &amp;quot;approvals not visible&amp;quot;; billing under never does not"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Codex itself has two ways to tell you, both in its config: &lt;code&gt;tui.notifications&lt;/code&gt; "can optionally filter by event type (for example, &lt;code&gt;agent-turn-complete&lt;/code&gt; and &lt;code&gt;approval-requested&lt;/code&gt;)", and &lt;code&gt;notify&lt;/code&gt; runs a program of yours, though only for &lt;code&gt;agent-turn-complete&lt;/code&gt;, with a JSON argument. Codex's &lt;code&gt;app-server&lt;/code&gt; also reports a waiting status; reading that from ccdeck is open work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When a session closed.&lt;/strong&gt; Nothing is written when you close the terminal. ccdeck keeps such a card LIVE until 90 minutes pass with no new line.&lt;/p&gt;

&lt;p&gt;ccdeck also draws no title for a Codex session, which a Claude Code card has, and joins a Codex sub-agent to its parent only when the file names that parent (&lt;code&gt;parent_thread_id&lt;/code&gt; in its &lt;code&gt;session_meta&lt;/code&gt;, or a sub-agent &lt;code&gt;started&lt;/code&gt; event in the parent's file).&lt;/p&gt;




&lt;h2&gt;
  
  
  The one write: refreshing the token safely
&lt;/h2&gt;

&lt;p&gt;Reading Codex's quota needs a live token from &lt;code&gt;$CODEX_HOME/auth.json&lt;/code&gt;, and tokens expire. Refreshing one from outside the CLI is where a reader can do damage: OpenAI's refresh tokens are single-use, so a rotation that never reaches disk leaves &lt;code&gt;auth.json&lt;/code&gt; holding a token that no longer works, and the next step is &lt;code&gt;codex login&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;ccdeck's refresh is a pattern worth copying for any file another program also writes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only when needed: the token is within 90 seconds of expiring, or the usage endpoint turned it down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One refresh at a time&lt;/strong&gt;, and the file is &lt;strong&gt;re-read inside a lock&lt;/strong&gt;, in case &lt;code&gt;codex&lt;/code&gt; refreshed it a moment earlier.&lt;/li&gt;
&lt;li&gt;The write is &lt;strong&gt;atomic&lt;/strong&gt;, and only the token fields are written; nothing else in &lt;code&gt;auth.json&lt;/code&gt; is touched.&lt;/li&gt;
&lt;li&gt;It happens only while a deck page is open or a quota notification is on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The quota needs Codex signed in with &lt;code&gt;codex login&lt;/code&gt; and a ChatGPT account. With an API key there is no quota to read.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why read files instead of installing a hook
&lt;/h2&gt;

&lt;p&gt;The rollout file is on every system Codex runs on, and reading it installs nothing and asks for no trust prompt; starting ccdeck writes nothing in the Codex folder (the token refresh above is the one write, and only when needed). The cost of following rollout files is described above: a file written for Codex's own history tells you what happened, not what Codex is waiting on.&lt;/p&gt;

&lt;p&gt;If you try this yourself, the short version: &lt;code&gt;ls -t&lt;/code&gt; for the newest file, &lt;code&gt;tail -F&lt;/code&gt; it, key off &lt;code&gt;type&lt;/code&gt; and &lt;code&gt;payload.type&lt;/code&gt;, take the model and policy from &lt;code&gt;turn_context&lt;/code&gt;, and name quota windows by their minutes.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI assistance was used to prepare this article and check it against the cited source code and documentation.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>codex</category>
      <category>openai</category>
      <category>cli</category>
      <category>ai</category>
    </item>
    <item>
      <title>Does Claude Secretly Score Your Account? We Checked the Viral 18-Rule List</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Thu, 29 Oct 2026 15:58:30 +0000</pubDate>
      <link>https://dev.to/bargan/does-claude-secretly-score-your-account-we-checked-the-viral-18-rule-list-3g25</link>
      <guid>https://dev.to/bargan/does-claude-secretly-score-your-account-we-checked-the-viral-18-rule-list-3g25</guid>
      <description>&lt;p&gt;On October 28, a Chinese-language &lt;a href="https://x.com/mashiro_0619/status/2115293730941067637" rel="noopener noreferrer"&gt;post on X&lt;/a&gt; offered 18 rules for keeping your Claude account alive.&lt;/p&gt;

&lt;p&gt;It claimed that Linux costs you points. That upgrading to Max too early is risky. That signing in on multiple devices hurts your account, while using Claude on mobile or enabling memory can help it.&lt;/p&gt;

&lt;p&gt;The explanation behind all of this was a hidden score. According to the post, Anthropic assigns points to accounts, adjusts them as people use the product, and suspends accounts that fall below a threshold.&lt;/p&gt;

&lt;p&gt;I understand why that caught on. If Claude Code is part of your workday, losing access halfway through a feature can be expensive. A list of 18 rules seems more useful than a policy page.&lt;/p&gt;

&lt;p&gt;The post does not provide a leaked document, an observable score, or a reproducible experiment. I reviewed the claims against Anthropic's published documentation and its account-enforcement statistics. &lt;strong&gt;The suspensions are real. The specific points system is not publicly verified.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Anthropic actually says about account bans
&lt;/h2&gt;

&lt;p&gt;Anthropic's &lt;a href="https://www.anthropic.com/transparency/system-trust-reporting" rel="noopener noreferrer"&gt;Transparency Hub&lt;/a&gt; says its Safeguards Team uses detection and monitoring systems to enforce policies. It can issue warnings, suspend accounts, or terminate access.&lt;/p&gt;

&lt;p&gt;That establishes that enforcement exists. It does not tell us whether consumer accounts have a persistent numerical score, whether Linux has a negative weight, or whether a Max upgrade triggers an observation period.&lt;/p&gt;

&lt;p&gt;Its &lt;a href="https://support.claude.com/en/articles/8241253-safeguards-warnings-and-appeals" rel="noopener noreferrer"&gt;safeguards and appeals documentation&lt;/a&gt; lists possible reasons for account bans, including repeated Usage Policy violations, account creation from an unsupported location, and violations of its Terms of Service.&lt;/p&gt;

&lt;p&gt;A separate &lt;a href="https://support.claude.com/en/articles/14328960-identity-verification-on-claude" rel="noopener noreferrer"&gt;identity-verification article&lt;/a&gt;, dated August 11, 2026, confirms that some users may be asked to verify their identity or business for platform-integrity, safety, or compliance purposes. &lt;strong&gt;A verification prompt alone is not proof that an account is about to be banned.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Anthropic’s published enforcement numbers
&lt;/h2&gt;

&lt;p&gt;For January through June 2026, Anthropic reported:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measure&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Banned accounts&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;11.4 million&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Appeals&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;398,000&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Appeal overturns&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;42,000&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Source: &lt;a href="https://www.anthropic.com/transparency/system-trust-reporting" rel="noopener noreferrer"&gt;Anthropic, System Trust and Reporting&lt;/a&gt;, last updated July 23, 2026.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyhu4n23s6if1s8oujt7m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyhu4n23s6if1s8oujt7m.png" alt="Anthropic reported 11.4 million banned accounts, 398,000 appeals and 42,000 appeal overturns for January–June 2026." width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The overturn count divided by the appeal count is about &lt;strong&gt;10.6%&lt;/strong&gt;. That is a ratio of two published numbers, &lt;strong&gt;not&lt;/strong&gt; a measured false-positive rate.&lt;/p&gt;

&lt;p&gt;We don't know how many people with erroneous bans never appealed, how enforcement categories are distributed, or whether every figure maps cleanly to unique individuals. The statistics show the scale of enforcement and the appeal process. They cannot establish the viral post's alleged scoring formula.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking the 18 claims against public documentation
&lt;/h2&gt;

&lt;p&gt;Rather than publish the alleged point values as instructions, I grouped the 18 claims by what they ask users to change.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Claim group&lt;/th&gt;
&lt;th&gt;Evidence check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Starting score, thresholds, observation periods, forced logouts&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Unverified.&lt;/strong&gt; No published consumer score table or thresholds.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email provider, IP quality, account age, payment method&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Unverified weights.&lt;/strong&gt; Account and region policies do not establish these bonuses or penalties.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delay Pro; Max upgrades trigger monitoring&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Unverified.&lt;/strong&gt; No official evidence of a fixed upgrade penalty.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mobile, desktop, memory, prepaid credits add points&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Unverified.&lt;/strong&gt; No documented rewards for these actions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multiple devices or Linux subtract points&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Unverified.&lt;/strong&gt; Claude Code officially supports Linux.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concurrency, repeated prompts, network changes cause deductions&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Unverified specifics.&lt;/strong&gt; Exact triggers and weights are undisclosed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time zone, language, nationality have little effect&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Unverified.&lt;/strong&gt; Claims that variables are ignored also need evidence.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heavy Claude Fable use subtracts points&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Unverified.&lt;/strong&gt; No public evidence of a product-specific deduction.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is an evidence check, not a claim that the underlying system works differently. Anthropic may use internal fraud or abuse signals that it does not publish. We simply cannot infer this particular formula from the available evidence.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fty28ttwdpbsgtz1o3l59.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fty28ttwdpbsgtz1o3l59.jpg" alt="Conceptual illustration of investigating an unverified account scoring claim." width="799" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Linux claim is a good reality check
&lt;/h3&gt;

&lt;p&gt;The post says Linux usage subtracts points. Anthropic's own &lt;a href="https://code.claude.com/docs/en/setup" rel="noopener noreferrer"&gt;Claude Code setup documentation&lt;/a&gt; provides Linux installation instructions, including Debian/Ubuntu and Alpine-specific guidance.&lt;/p&gt;

&lt;p&gt;Official support does not prove that operating-system data could never be used in any safety system. It does mean there is no basis for telling developers to abandon a supported OS to protect an undocumented score.&lt;/p&gt;

&lt;p&gt;I would not change my development environment based on that claim.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Max story confuses sequence with cause
&lt;/h3&gt;

&lt;p&gt;The post suggests that buying Max quickly, or upgrading to it, puts an account under observation.&lt;/p&gt;

&lt;p&gt;Imagine a developer subscribes to Max and gets suspended the following day. That gives us an order of events, not an explanation. The account may have had an unrelated policy issue; the timing may be coincidental. Without a comparison group and evidence of the actual decision, assigning the cause to the upgrade is speculation.&lt;/p&gt;

&lt;p&gt;That distinction matters when a thread recommends changing payment or subscription habits. A confident workaround is useless if the proposed trigger was never established.&lt;/p&gt;

&lt;h3&gt;
  
  
  IP changes and identity verification need separate treatment
&lt;/h3&gt;

&lt;p&gt;Anthropic explicitly names unsupported-location account creation as a potential enforcement reason. You should verify whether your location and intended use are eligible under its &lt;a href="https://www.anthropic.com/legal/supported-regions" rel="noopener noreferrer"&gt;Supported Regions Policy&lt;/a&gt;. As of this review, the linked page displays a version effective November 12, 2026, so check which policy version applies when you use the service.&lt;/p&gt;

&lt;p&gt;That does &lt;strong&gt;not&lt;/strong&gt; demonstrate a fixed penalty for switching between home Wi-Fi and mobile data, using a second computer, or going through an ordinary identity check. The list's advice about a particular proxy configuration is unsupported as account-safety guidance.&lt;/p&gt;

&lt;h2&gt;
  
  
  One useful detail from Anthropic's API documentation
&lt;/h2&gt;

&lt;p&gt;There &lt;em&gt;is&lt;/em&gt; a documented enforcement status in Anthropic's developer platform.&lt;/p&gt;

&lt;p&gt;For businesses that attribute API requests to their own end customers, the &lt;a href="https://platform.claude.com/docs/en/manage-claude/user-profiles" rel="noopener noreferrer"&gt;User Profiles API documentation&lt;/a&gt; describes an &lt;code&gt;enforcement_status&lt;/code&gt; field with these values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unrestricted
restricted
banned
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also documents enforcement reasons such as &lt;code&gt;unsupported_region&lt;/code&gt;, &lt;code&gt;fraud_or_abuse&lt;/code&gt;, and &lt;code&gt;usage_policy_violation&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These fields describe enforcement on &lt;strong&gt;API end-customer profiles&lt;/strong&gt;. They are &lt;strong&gt;not&lt;/strong&gt; evidence that your personal Claude account has a numerical score, and they do not expose any score or weights.&lt;/p&gt;

&lt;p&gt;The public API shows how one kind of enforcement outcome is represented. It does not reveal the internal model that produced the outcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I would assess a claim like this
&lt;/h2&gt;

&lt;p&gt;A credible explanation of the purported scoring rules would need more than suspended-account screenshots and stories about what happened right before a ban.&lt;/p&gt;

&lt;p&gt;At minimum, I would look for authenticated documentation, a clear official statement, or independently reproducible data with a defined sample. A credible study would have to separate subscription type from region, account age, actual policy violations, and other confounding factors. A convenience sample of people reporting bans online cannot establish the effect of one OS or subscription plan.&lt;/p&gt;

&lt;p&gt;Nor should anyone intentionally trigger policy violations just to reverse-engineer enforcement.&lt;/p&gt;

&lt;h2&gt;
  
  
  What developers can do today
&lt;/h2&gt;

&lt;p&gt;A disputed account rule is a poor reason to rebuild your workflow. There are a few precautions that pay off even if the viral thread turns out to be entirely wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep the work in your repository.&lt;/strong&gt; Commit code, configuration, and architecture decisions. Keep prompts or agent instructions that you need to reuse as versioned files. If a useful decision exists only in a chat transcript, write it down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep verification independent of the assistant.&lt;/strong&gt; For a .NET project, that may be as straightforward as a documented &lt;code&gt;dotnet test&lt;/code&gt; command in CI, a reproducible build, and a short runbook for local setup. A teammate should be able to verify the project without signing into your AI account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Export conversations when they matter.&lt;/strong&gt; Anthropic documents &lt;strong&gt;Settings → Privacy → Export data&lt;/strong&gt; for personal Free, Pro, and Max users on web or desktop. Its &lt;a href="https://support.claude.com/en/articles/9450526-export-your-claude-data" rel="noopener noreferrer"&gt;export guide&lt;/a&gt; also says you cannot import that export into a different personal Claude account. It is an archive, not a one-click migration tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Know the official appeal route.&lt;/strong&gt; Anthropic directs banned users to sign in to Claude and submit the &lt;a href="https://support.claude.com/en/articles/8241253-safeguards-warnings-and-appeals" rel="noopener noreferrer"&gt;appeal form&lt;/a&gt;. Some export data may remain available after a ban, subject to restrictions described by Anthropic. The appeal form is more useful than changing operating systems in response to a rumor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5x0ikjwqew3vkok91xyl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5x0ikjwqew3vkok91xyl.png" alt="Checklist: keep code in Git, maintain reproducible tests, export important conversations and use official appeals." width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd like Anthropic to explain
&lt;/h2&gt;

&lt;p&gt;The transparency report is helpful, but developers still have questions. How often are suspensions later overturned? How quickly do users receive a review? How does the company identify and correct false positives? What information can it share about enforcement without making its safeguards easier to evade?&lt;/p&gt;

&lt;p&gt;Those are reasonable requests even if the secret-score story turns out to be fiction.&lt;/p&gt;

&lt;p&gt;If access to Claude vanished tomorrow, I would want my code, tests, build instructions, and project decisions ready to use elsewhere. I would spend time on that before following an 18-point account-survival ritual that nobody has verified.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Research note (October 29, 2026): The original &lt;a href="https://x.com/mashiro_0619/status/2115293730941067637" rel="noopener noreferrer"&gt;X post&lt;/a&gt; is in Chinese; its 18 claims were cross-checked against a &lt;a href="https://blog.eimoon.com/p/claude-risk-control-points-rumor/" rel="noopener noreferrer"&gt;contemporaneous reproduction of the text&lt;/a&gt; and Anthropic's public documentation. Anthropic has not publicly confirmed the specific scoring mechanism alleged in the thread. The illustrations are conceptual, not screenshots of Anthropic's internal systems.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>security</category>
      <category>discuss</category>
    </item>
    <item>
      <title>What Claude Code's Agent View lists, what it leaves out, and how to see the rest</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Thu, 29 Oct 2026 06:01:56 +0000</pubDate>
      <link>https://dev.to/bargan/what-claude-codes-agent-view-lists-what-it-leaves-out-and-how-to-see-the-rest-1d5m</link>
      <guid>https://dev.to/bargan/what-claude-codes-agent-view-lists-what-it-leaves-out-and-how-to-see-the-rest-1d5m</guid>
      <description>&lt;p&gt;This article was prepared with AI assistance, using ccdeck’s source code and documentation to check the technical details.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I build &lt;a href="https://github.com/BarganConstantin/ccdeck" rel="noopener noreferrer"&gt;ccdeck&lt;/a&gt;, one of the tools that shows what Agent View leaves out, so read its part as one side of the comparison; the short version, with every source, is at &lt;a href="https://ccdeck.dev/agent-view/" rel="noopener noreferrer"&gt;ccdeck.dev/agent-view&lt;/a&gt;. Agent View is quoted from &lt;a href="https://code.claude.com/docs/en/agent-view" rel="noopener noreferrer"&gt;Anthropic's documentation&lt;/a&gt; as I read it on 29 October 2026, the &lt;code&gt;--json&lt;/code&gt; output is from Claude Code 2.1.312, and ccdeck is described as of 3.49.1.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What Agent View is
&lt;/h2&gt;

&lt;p&gt;From the docs: "Agent view, opened with &lt;code&gt;claude agents&lt;/code&gt;, is one screen for all your background sessions: what's running, what needs your input, and what's done." It is new, and the docs say so up front: "Agent view is in research preview. The interface and keyboard shortcuts may change as the feature evolves."&lt;/p&gt;

&lt;p&gt;What it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start work.&lt;/strong&gt; Type a task and press &lt;code&gt;Enter&lt;/code&gt;, and a new background session starts on it. From a shell, &lt;code&gt;claude --bg "&amp;lt;prompt&amp;gt;"&lt;/code&gt; does the same.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sort by who needs you.&lt;/strong&gt; "Agent view groups sessions so the ones that need input are at the top." The &lt;strong&gt;Needs input&lt;/strong&gt; group covers questions, permission decisions and other prompts only you can answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reply in place.&lt;/strong&gt; &lt;code&gt;Space&lt;/code&gt; on a row opens a peek panel with "the exact question it's asking, above the reply input"; type a reply and press &lt;code&gt;Enter&lt;/code&gt; to send it without leaving agent view.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attach and leave.&lt;/strong&gt; &lt;code&gt;→&lt;/code&gt; attaches to the whole conversation; &lt;code&gt;←&lt;/code&gt; on an empty prompt detaches, and the session keeps running.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it works underneath
&lt;/h2&gt;

&lt;p&gt;The sessions in Agent View are not running in your terminal. A &lt;strong&gt;supervisor process&lt;/strong&gt; runs them, "so they keep working after you close agent view or your terminal"; Claude Code starts it the first time you background a session or open agent view. Each session is its own Claude Code process under it, and in a git repository each one edits files in its own worktree by default. State lives under your config directory: the supervisor's log in &lt;code&gt;~/.claude/daemon.log&lt;/code&gt;, the list of running sessions in &lt;code&gt;~/.claude/daemon/roster.json&lt;/code&gt;, and each session's state in &lt;code&gt;~/.claude/jobs/&amp;lt;id&amp;gt;/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two consequences matter for what comes next. A background session "reads its settings from the directory it runs in, the same as if you had started &lt;code&gt;claude&lt;/code&gt; there", so your user settings, hooks included, apply to it. And the files under &lt;code&gt;~/.claude/jobs/&amp;lt;id&amp;gt;/&lt;/code&gt; "are not a stable interface"; the docs point scripts elsewhere, as below.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it doesn't list
&lt;/h2&gt;

&lt;p&gt;The same page, in the section on monitoring:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Interactive sessions you have open in other terminals don't appear until you background them. Subagents and teammates a session spawns aren't listed as separate rows.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So three things stay off the screen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sessions in your other terminals.&lt;/strong&gt; A &lt;code&gt;claude&lt;/code&gt; in a tmux pane, an editor's terminal or a second window is invisible until you run &lt;code&gt;/bg&lt;/code&gt; in it or press &lt;code&gt;←&lt;/code&gt; on an empty prompt. That turns it into a background session, run by the supervisor: a change to how it runs, not only to where you see it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subagents.&lt;/strong&gt; A session that fans out into four subagents is one row.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codex.&lt;/strong&gt; Agent View lists Claude Code sessions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A row shows "the session's name, current activity, and its age": no spend or quota, while the docs warn that "background sessions consume your subscription usage the same as interactive sessions, so running ten agents in parallel uses quota roughly ten times as fast as running one."&lt;/p&gt;




&lt;h2&gt;
  
  
  Seeing the rest with Claude Code alone
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Your other terminals: &lt;code&gt;claude agents --json&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The screen leaves interactive sessions out; the JSON listing does not. In 2.1.312 its help reads: "Print active sessions (interactive and background) as a JSON array and exit". The docs call it "the supported way to read session state from outside Claude Code". Each entry has a &lt;code&gt;kind&lt;/code&gt; (&lt;code&gt;interactive&lt;/code&gt; or &lt;code&gt;background&lt;/code&gt;), and while its process is alive a &lt;code&gt;status&lt;/code&gt; (&lt;code&gt;busy&lt;/code&gt;, &lt;code&gt;waiting&lt;/code&gt; or &lt;code&gt;idle&lt;/code&gt;); a waiting session adds &lt;code&gt;waitingFor&lt;/code&gt;, such as &lt;code&gt;permission prompt&lt;/code&gt; or &lt;code&gt;input needed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Every session waiting on you, from any terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude agents &lt;span class="nt"&gt;--json&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.[] | select(.status == "waiting") | [.kind, .name, .waitingFor] | @tsv'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It lists sessions, not subagents, and it is a snapshot: poll it to keep it current.&lt;/p&gt;

&lt;h3&gt;
  
  
  Waiting sessions as they happen: the &lt;code&gt;Notification&lt;/code&gt; hook
&lt;/h3&gt;

&lt;p&gt;A hook in &lt;code&gt;~/.claude/settings.json&lt;/code&gt; applies to every session that reads that file, whichever terminal started it, background sessions included. The &lt;code&gt;Notification&lt;/code&gt; event tells you when one stops for you, with a &lt;code&gt;notification_type&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;permission_prompt&lt;/code&gt;: "Claude needs you to approve a tool use", once the prompt "has waited about six seconds". In a terminal each keystroke defers it; for an immediate signal, the docs point to &lt;code&gt;PermissionRequest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;idle_prompt&lt;/code&gt;: Claude "finished responding about 60 seconds ago and you haven't typed since".&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;agent_needs_input&lt;/code&gt; and &lt;code&gt;agent_completed&lt;/code&gt;: for background sessions, and only "while agent view is open in a terminal".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A hook that appends one line per wait to a log file, and prints nothing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&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;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Notification"&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;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"permission_prompt|idle_prompt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&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;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jq -c '{at: (now | todate), session: .session_id, cwd, type: .notification_type, message}' &amp;gt;&amp;gt; ~/.claude/waiting.log"&lt;/span&gt;&lt;span class="w"&gt;
          &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;span class="w"&gt;
      &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;span class="w"&gt;
  &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;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notification hooks "can't block or modify notifications", so this one can only observe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subagents: tool events carry who made them
&lt;/h3&gt;

&lt;p&gt;Hooks from settings files "also run inside subagents", and tool events such as &lt;code&gt;PreToolUse&lt;/code&gt; carry &lt;code&gt;agent_id&lt;/code&gt; and &lt;code&gt;agent_type&lt;/code&gt; when a subagent made the call. &lt;code&gt;SubagentStart&lt;/code&gt; and &lt;code&gt;SubagentStop&lt;/code&gt; mark each one's life. That is enough to draw a session as the tree it is.&lt;/p&gt;

&lt;h3&gt;
  
  
  Codex
&lt;/h3&gt;

&lt;p&gt;Neither listing has it. Codex CLI has its own &lt;code&gt;codex agents&lt;/code&gt; dashboard, and writes every session to a rollout file under &lt;code&gt;~/.codex/sessions&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  One tool that does the hook route: ccdeck
&lt;/h2&gt;

&lt;p&gt;ccdeck is a free, open-source dashboard that runs on your machine. Its first run adds a hook to &lt;code&gt;~/.claude/settings.json&lt;/code&gt; for up to twelve events, &lt;code&gt;Notification&lt;/code&gt; among them, so every Claude Code session that reads that file reports to it, and you keep starting sessions the way you do now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Each subagent is a card of its own&lt;/strong&gt;, joined to its session by a line, with its last four tool calls beside it while they run:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff3z6qbfjyklv81dswszc.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff3z6qbfjyklv81dswszc.webp" alt="The web-api session on the canvas, with lines to four subagent cards: migrator, docs and general-purpose marked DONE on solid lines, and reviewer marked LIVE on a dashed line. Tool bubbles sit to the right of every card." width="800" height="358"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data. One session with four subagents, of which only the reviewer is still running.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The topbar names the sessions stopped on you&lt;/strong&gt;, with how long each has waited and on what, and the session list puts prompts and questions first, then finished turns, longest wait first. The limits: that list is Claude Code only, and the tool name beside a wait is ccdeck's inference, not something Claude Code reports. In a terminal, &lt;code&gt;ccdeck sessions&lt;/code&gt; prints the same list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Codex sessions sit on the same canvas&lt;/strong&gt;, read from their rollout files with no hook, and a Codex sub-agent whose rollout names its parent gets a card joined to it. Codex writes no approval request there, so a running Codex session that can ask for one says &lt;strong&gt;approvals not visible&lt;/strong&gt; rather than leaving the waiting list to look complete.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A background session's own line.&lt;/strong&gt; For a session started with &lt;code&gt;claude --bg&lt;/code&gt; or from &lt;code&gt;claude agents&lt;/code&gt;, ccdeck also reads its folder in &lt;code&gt;~/.claude/jobs&lt;/code&gt; and shows Claude Code's own line for it: the question it is stuck on and the reply Claude Code suggests, or the headline of what it got done. No model is called for it. Because those files are not a stable interface, ccdeck treats every field as untrusted, and a file it cannot parse means no line for that session.&lt;/p&gt;

&lt;p&gt;Beside that, an estimated spend per session (the tokens priced at API rates) and the Claude and Codex quota windows as they refill.&lt;/p&gt;

&lt;p&gt;What it cannot do, by design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reply, start or attach.&lt;/strong&gt; Its hook is a one-way forwarder: it posts the event, exits &lt;code&gt;0&lt;/code&gt; and writes nothing to stdout, the two channels a Claude Code hook has to answer with. You answer in the session's terminal, or in Agent View. The one thing the deck runs is &lt;strong&gt;Continue&lt;/strong&gt;, on a background session whose turn stopped on an error such as an expired login or a usage limit: it stops the session if Claude Code still lists it, then runs &lt;code&gt;claude --resume &amp;lt;id&amp;gt; --bg "Continue from where you left off."&lt;/code&gt; in its folder, at your press or, with a setting you turn on, once per stop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Record while stopped.&lt;/strong&gt; The hook forwards to a running deck and keeps nothing; events sent while no deck runs are gone.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Side by side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Agent View&lt;/th&gt;
&lt;th&gt;&lt;code&gt;claude agents --json&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;A hook-based dashboard (ccdeck)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sessions in other terminals&lt;/td&gt;
&lt;td&gt;Only once backgrounded&lt;/td&gt;
&lt;td&gt;Yes, with &lt;code&gt;status&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Yes, whichever terminal started them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subagents&lt;/td&gt;
&lt;td&gt;Not as rows&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Each one a card, joined to its session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex CLI&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes, from its rollout files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Waiting on you&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Needs input&lt;/strong&gt; group&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;status: waiting&lt;/code&gt; and &lt;code&gt;waitingFor&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Claude Code only, longest wait first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reply, start, attach&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (other &lt;code&gt;claude&lt;/code&gt; commands attach and stop)&lt;/td&gt;
&lt;td&gt;No; Continue resumes a background session that stopped on an error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spend and quota&lt;/td&gt;
&lt;td&gt;Not in the list&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Estimated spend per session; quota windows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Use them together
&lt;/h2&gt;

&lt;p&gt;They do not collide. A read-only hook answers nothing, so it cannot change what Agent View or its sessions do, and because background sessions read your user settings, the sessions Agent View runs report to the hook too.&lt;/p&gt;

&lt;p&gt;The split I would suggest: dispatch and reply in Agent View; poll &lt;code&gt;claude agents --json&lt;/code&gt; if all you need is "who is waiting, in any terminal"; and add a hook, your own or a dashboard's, when you want subagents, Codex or spend beside them.&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>cli</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
    <item>
      <title>What Claude Code's telemetry sends, and how to see it for yourself</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Tue, 27 Oct 2026 18:18:53 +0000</pubDate>
      <link>https://dev.to/bargan/what-claude-codes-telemetry-sends-and-how-to-see-it-for-yourself-3434</link>
      <guid>https://dev.to/bargan/what-claude-codes-telemetry-sends-and-how-to-see-it-for-yourself-3434</guid>
      <description>&lt;p&gt;This article was prepared with AI assistance, using ccdeck’s source code and documentation to check the technical details.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I build &lt;a href="https://github.com/BarganConstantin/ccdeck" rel="noopener noreferrer"&gt;ccdeck&lt;/a&gt;, whose Telemetry Radar is one of the ways to check, near the end; the short version of this post, with every source, is at &lt;a href="https://ccdeck.dev/claude-code-telemetry/" rel="noopener noreferrer"&gt;ccdeck.dev/claude-code-telemetry&lt;/a&gt;. Every quote is from Anthropic's &lt;a href="https://code.claude.com/docs/en/data-usage#telemetry-services" rel="noopener noreferrer"&gt;Data usage&lt;/a&gt;, &lt;a href="https://code.claude.com/docs/en/monitoring-usage" rel="noopener noreferrer"&gt;Monitoring&lt;/a&gt;, &lt;a href="https://code.claude.com/docs/en/env-vars" rel="noopener noreferrer"&gt;Environment variables&lt;/a&gt; and &lt;a href="https://code.claude.com/docs/en/settings-reference" rel="noopener noreferrer"&gt;Settings&lt;/a&gt; pages as I read them on 14 October 2026, and ccdeck as of 3.48.0. The docs change often.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Two things called telemetry
&lt;/h2&gt;

&lt;p&gt;"Does Claude Code send my prompts somewhere?" has two answers, because Claude Code's docs use the word &lt;em&gt;telemetry&lt;/em&gt; for two things that work in opposite directions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Operational telemetry&lt;/strong&gt; (usage metrics and error reports) goes to Anthropic and third parties. On the Claude API it is on until you turn it off, error reports only on Pro and Max sign-ins, and the metrics carry no code, prompts or file paths.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The OpenTelemetry export&lt;/strong&gt; goes to a collector you choose. It is off until you turn it on, and can carry your prompts and tool input if you add the flags for them.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Claude Code sends on its own
&lt;/h2&gt;

&lt;p&gt;In the docs' words: "Claude Code sends two kinds of operational telemetry: usage metrics and error reports."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Metrics&lt;/strong&gt;: "latency, reliability, and usage patterns, sent to Anthropic and to third-party logging infrastructure over TLS. Metrics never include your code, prompts, or file paths." On the Claude API they are on by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error reports&lt;/strong&gt;: "error messages and stack traces from Claude Code's own internals, sent to a third-party error tracking service over TLS." Claude Code redacts "known patterns of secrets, file paths, email addresses, and other personal information before anything leaves your machine."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Error reports are on only when all four hold: you sign in with Pro or Max, you run Claude Code v2.1.198 or later, you connect directly to the Claude API, and your organization has no zero data retention or HIPAA agreement. On Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry and Claude Platform on AWS, both start off.&lt;/p&gt;

&lt;p&gt;To turn them off, set two variables in your shell or in the &lt;code&gt;env&lt;/code&gt; block of &lt;code&gt;~/.claude/settings.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"env"&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;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"DISABLE_TELEMETRY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"DISABLE_ERROR_REPORTING"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="w"&gt;
  &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;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or set &lt;code&gt;CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1&lt;/code&gt;, which turns both off at once, with auto-updates, release notes and &lt;code&gt;/feedback&lt;/code&gt;. One cost: it and &lt;code&gt;DISABLE_TELEMETRY&lt;/code&gt; also stop feature-flag fetching, "which can make Remote Control unavailable"; &lt;code&gt;DISABLE_ERROR_REPORTING&lt;/code&gt; does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap: "0" still opts out
&lt;/h2&gt;

&lt;p&gt;Most on/off variables in Claude Code read &lt;code&gt;0&lt;/code&gt; as off. A few do not, and three of them are the switches above:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Some variables read only whether you set them at all, so any non-empty value including &lt;code&gt;0&lt;/code&gt; turns the behavior on, and you turn the behavior off by unsetting the variable or setting it to an empty value.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The list under that sentence starts with &lt;code&gt;CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC&lt;/code&gt;, &lt;code&gt;DISABLE_TELEMETRY&lt;/code&gt; and &lt;code&gt;DISABLE_ERROR_REPORTING&lt;/code&gt;. Each of them turns something &lt;em&gt;off&lt;/em&gt;, so &lt;code&gt;"DISABLE_TELEMETRY": "0"&lt;/code&gt; still opts you out. To turn metrics back on, delete the key or leave it empty.&lt;/p&gt;

&lt;h2&gt;
  
  
  What else leaves the machine
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The model request itself "includes all user prompts and model outputs, encrypted in transit via TLS 1.2+." No switch above changes that.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/feedback&lt;/code&gt;, &lt;code&gt;/bug&lt;/code&gt; and &lt;code&gt;/share&lt;/code&gt; send Anthropic "a copy of your conversation history including code" when you run them. &lt;code&gt;DISABLE_FEEDBACK_COMMAND=1&lt;/code&gt; turns them off.&lt;/li&gt;
&lt;li&gt;The "How is Claude doing this session?" survey "records only your rating". A follow-up can offer to upload the session transcript, and "Nothing is uploaded unless you explicitly select &lt;strong&gt;Yes&lt;/strong&gt;". &lt;code&gt;CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY=1&lt;/code&gt; turns the surveys off, and so do &lt;code&gt;DISABLE_TELEMETRY&lt;/code&gt;, &lt;code&gt;DO_NOT_TRACK&lt;/code&gt; and &lt;code&gt;CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Before WebFetch reads a page, it sends the hostname alone to &lt;code&gt;api.anthropic.com&lt;/code&gt; for a safety check. &lt;code&gt;CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC&lt;/code&gt; does not stop it; &lt;code&gt;"skipWebFetchPreflight": true&lt;/code&gt; in your settings does.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  OpenTelemetry: off until you turn it on
&lt;/h2&gt;

&lt;p&gt;Claude Code can also export metrics, events and (in beta) traces through OpenTelemetry, for your own monitoring. This export "is opt-in and requires explicit configuration", starting with &lt;code&gt;CLAUDE_CODE_ENABLE_TELEMETRY=1&lt;/code&gt;, and its data is "sent only to the OTel endpoint you configure, never to Anthropic."&lt;/p&gt;

&lt;p&gt;"Claude Code has no default protocol", so set one for each &lt;code&gt;otlp&lt;/code&gt; exporter. By default an event records how long a prompt or a response was, not its text: "User prompt content is not collected by default. Only prompt length is recorded." Each kind of content has its own flag, and each is off by default:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;What it adds to the export&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OTEL_LOG_USER_PROMPTS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;your prompts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OTEL_LOG_ASSISTANT_RESPONSES&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Claude's responses; when unset, it follows &lt;code&gt;OTEL_LOG_USER_PROMPTS&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OTEL_LOG_TOOL_DETAILS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tool input: Bash commands, file paths, URLs, MCP server and tool names&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OTEL_LOG_TOOL_CONTENT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;what tools return, in trace spans (needs tracing)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OTEL_LOG_RAW_API_BODIES&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the full API requests and responses: "Bodies include the entire conversation history"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Signed in with OAuth, the exports also carry your e-mail address as &lt;code&gt;user.email&lt;/code&gt;. And since v2.1.282, Claude Code ignores these variables in a repository's &lt;code&gt;.claude/settings.json&lt;/code&gt;, "so a repository can't use them to turn telemetry on, choose where it goes, or capture content." A repository can still switch a signal off.&lt;/p&gt;




&lt;h2&gt;
  
  
  Check what your machine is set to send
&lt;/h2&gt;

&lt;p&gt;The flags can sit in more than one place, and a line added months ago stays there, out of sight. Three places to look:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Your shell.&lt;/strong&gt; Anything exported in your profile.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;~/.claude/settings.json&lt;/code&gt;&lt;/strong&gt;, under &lt;code&gt;env&lt;/code&gt;. A value there "overwrites the same variable exported in your shell", so this file wins over step 1 (the Claude Desktop app's launch environment is the exception).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managed settings&lt;/strong&gt;, if your organization deploys them. You cannot override those.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;env&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'^(CLAUDE_CODE_ENABLE_TELEMETRY|OTEL_|DISABLE_TELEMETRY|DISABLE_ERROR_REPORTING|CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC)='&lt;/span&gt;

jq &lt;span class="s1"&gt;'.env // {} | with_entries(select(.key | test("^(CLAUDE_CODE_ENABLE_TELEMETRY|OTEL_|DISABLE_TELEMETRY|DISABLE_ERROR_REPORTING|CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC)")))'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  ~/.claude/settings.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;CLAUDE_CODE_ENABLE_TELEMETRY&lt;/code&gt; shows up in either, look next for &lt;code&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/code&gt; (where it goes) and any &lt;code&gt;OTEL_LOG_*&lt;/code&gt; flag (what content it carries).&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch an export arrive
&lt;/h2&gt;

&lt;p&gt;Reading flags tells you what Claude Code is &lt;em&gt;set&lt;/em&gt; to send. To see what an export actually contains, point it at a collector on your own machine that prints everything it receives. The OpenTelemetry Collector's &lt;code&gt;debug&lt;/code&gt; exporter does that; this configuration, assembled from the &lt;a href="https://opentelemetry.io/docs/collector/configuration/" rel="noopener noreferrer"&gt;Collector's configuration docs&lt;/a&gt;, passes &lt;code&gt;otelcol validate&lt;/code&gt; on version 0.162.0:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;protocols&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;grpc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;127.0.0.1:4317&lt;/span&gt;
&lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;verbosity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;detailed&lt;/span&gt;
&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;logs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;debug&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;debug&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it with the release binary (&lt;code&gt;otelcol --config=config.yaml&lt;/code&gt;) or &lt;a href="https://opentelemetry.io/docs/collector/install/docker/" rel="noopener noreferrer"&gt;the Docker image&lt;/a&gt;, which reads its configuration from &lt;code&gt;/etc/otelcol/config.yaml&lt;/code&gt; (inside a container, listen on &lt;code&gt;0.0.0.0:4317&lt;/code&gt; and publish the port to &lt;code&gt;127.0.0.1&lt;/code&gt;). If something already holds port 4317 (ccdeck's own default port is 4317, for one), use another port in both places. Then start one session pointed at it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CLAUDE_CODE_ENABLE_TELEMETRY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;OTEL_METRICS_EXPORTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;otlp &lt;span class="nv"&gt;OTEL_LOGS_EXPORTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;otlp &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_PROTOCOL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;grpc &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://127.0.0.1:4317 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;OTEL_LOGS_EXPORT_INTERVAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1000 &lt;span class="se"&gt;\&lt;/span&gt;
claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Submit a prompt. The docs say to look for the &lt;code&gt;claude_code.user_prompt&lt;/code&gt; event, and without &lt;code&gt;OTEL_LOG_USER_PROMPTS&lt;/code&gt; it carries &lt;code&gt;prompt_length&lt;/code&gt;, not the text. Two caveats: an &lt;code&gt;env&lt;/code&gt; block in your settings that sets these variables wins over the ones on this command line, and if nothing arrives, &lt;code&gt;claude --debug-file &amp;lt;path&amp;gt;&lt;/code&gt; logs exporter failures as &lt;code&gt;[3P telemetry]&lt;/code&gt; errors (lines marked &lt;code&gt;[Anthropic telemetry]&lt;/code&gt; are the operational telemetry above, not your export).&lt;/p&gt;




&lt;h2&gt;
  
  
  Doing the same from a dashboard: Telemetry Radar
&lt;/h2&gt;

&lt;p&gt;The two checks above are what ccdeck's &lt;strong&gt;Telemetry Radar&lt;/strong&gt; (marked &lt;strong&gt;Beta&lt;/strong&gt;) does in one panel. Its &lt;strong&gt;Configuration&lt;/strong&gt; view lists each telemetry flag and where it was set (your user settings, the cached organization settings or the managed settings file, and on Linux the environment a running session was launched with), with the destinations; credentials and endpoint paths stay hidden. Those cached organization settings are how a Team or Enterprise organization turns the export on for its members, and ccdeck's accounts panel marks each account of such an organization with a small shield that says, on hover or focus, what it sends and where.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd5ur232y3h942z25bg6d.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd5ur232y3h942z25bg6d.webp" alt="Telemetry Radar's Configuration view: the destination http://192.0.2.16:4317 from User settings, then Telemetry and Your prompts enabled with their switches on, and a flag no settings file names marked Not set, its switch off" width="800" height="824"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data. Telemetry and prompts on in your own settings; a flag no file sets says Not set. 192.0.2.16 is an address reserved for examples.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Its &lt;strong&gt;Monitor&lt;/strong&gt; view does the second check without a collector of yours, and without a Start button: whenever ccdeck runs, it finds Claude Code's collector by itself, captures in the background (&lt;code&gt;tcpdump&lt;/code&gt; on macOS and Linux, Wireshark's &lt;code&gt;dumpcap&lt;/code&gt; on Windows) and decodes the exports it sees on their way to your collector. &lt;code&gt;AGENTS_DECK_NO_RADAR=1&lt;/code&gt; keeps a deck started with it from looking or capturing. Its limits are the honest part:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Settings files, and on Linux running sessions.&lt;/strong&gt; Elsewhere a variable exported only in your shell does not show, and on Linux it shows only while a session started with it runs, so "No destination found" does not mean telemetry is off.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Its switches write to your user settings.&lt;/strong&gt; Every telemetry and content flag has one, a flag no file sets shows &lt;strong&gt;Not set&lt;/strong&gt; with its switch off, and each switch writes &lt;code&gt;"1"&lt;/code&gt; or &lt;code&gt;"0"&lt;/code&gt; under &lt;code&gt;env&lt;/code&gt; in &lt;code&gt;~/.claude/settings.json&lt;/code&gt;, for new sessions; a flag from organization or managed settings shows &lt;strong&gt;Managed&lt;/strong&gt;, and one from a launch environment is read-only. It neither shows nor switches &lt;code&gt;DISABLE_TELEMETRY&lt;/code&gt; or &lt;code&gt;DISABLE_ERROR_REPORTING&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Only plaintext OTLP over gRPC, on IPv4.&lt;/strong&gt; An HTTPS export shows as a connection it cannot read; &lt;code&gt;http/protobuf&lt;/code&gt; and &lt;code&gt;http/json&lt;/code&gt; are not decoded. It cannot see Anthropic's own metrics and error reports, or model requests, which travel over TLS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not which program sent it.&lt;/strong&gt; It filters by address, so another app sending to the same collector shows too, and an empty list does not mean nothing was sent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capture needs an administrator once.&lt;/strong&gt; &lt;strong&gt;Allow capture…&lt;/strong&gt; asks for your password through the system's own prompt and changes the system: on macOS, a LaunchDaemon of ccdeck's own (&lt;code&gt;dev.ccdeck.chmodbpf&lt;/code&gt;) that does what Wireshark's ChmodBPF does, with you in the &lt;code&gt;access_bpf&lt;/code&gt; group; on Linux, &lt;code&gt;tcpdump&lt;/code&gt;, installed if missing and allowed to capture (&lt;code&gt;setcap&lt;/code&gt;) for your own group only. Over SSH it shows a &lt;code&gt;sudo&lt;/code&gt; command instead; on Windows you install Wireshark with Npcap yourself. What it decodes stays in memory, for 24 hours at most, and is never written to a file or sent anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Both outlast ccdeck.&lt;/strong&gt; Switching a flag off writes &lt;code&gt;"0"&lt;/code&gt; rather than removing the key, &lt;code&gt;npx ccdeck --uninstall&lt;/code&gt; takes only ccdeck's hooks out of &lt;code&gt;~/.claude/settings.json&lt;/code&gt;, and the capture setup stays until an administrator undoes it. For the OpenTelemetry flags &lt;code&gt;"0"&lt;/code&gt; does mean off; the "0 still opts out" rule is about the three &lt;code&gt;DISABLE&lt;/code&gt; variables, which Radar does not touch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And since this post is about what tools send: ccdeck sends usage reports of its own, on by default. They carry its version, your system, your IP address and (on all but the error reports) a device fingerprint, rough counts such as how many sessions ran in a day, and the errors it hits; your prompts, files and project names are not in them. &lt;code&gt;AGENTS_DECK_NO_REPORTS=1&lt;/code&gt; from the first start sends none. Since 3.42.0 the feedback you send from its Feedback button carries the same fingerprint, unless that variable is set.&lt;/p&gt;




&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Operational telemetry goes to Anthropic and third parties, on by default on the Claude API; &lt;code&gt;DISABLE_TELEMETRY&lt;/code&gt; and &lt;code&gt;DISABLE_ERROR_REPORTING&lt;/code&gt; turn it off, and &lt;code&gt;"0"&lt;/code&gt; counts as set.&lt;/li&gt;
&lt;li&gt;The OpenTelemetry export goes only where you point it, off until &lt;code&gt;CLAUDE_CODE_ENABLE_TELEMETRY=1&lt;/code&gt;, and carries content only with an &lt;code&gt;OTEL_LOG_*&lt;/code&gt; flag.&lt;/li&gt;
&lt;li&gt;Check your shell, then &lt;code&gt;~/.claude/settings.json&lt;/code&gt; (which wins over the shell), then managed settings (which win over both).&lt;/li&gt;
&lt;li&gt;To see what an export really holds, send it to a collector on &lt;code&gt;127.0.0.1&lt;/code&gt; with the &lt;code&gt;debug&lt;/code&gt; exporter.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claudecode</category>
      <category>opentelemetry</category>
      <category>privacy</category>
      <category>security</category>
    </item>
    <item>
      <title>Anthropic Runs 30,000 Agents at Once. What Keeps Them Under Control?</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Thu, 22 Oct 2026 12:15:07 +0000</pubDate>
      <link>https://dev.to/bargan/anthropic-runs-30000-agents-at-once-what-keeps-them-under-control-4aa8</link>
      <guid>https://dev.to/bargan/anthropic-runs-30000-agents-at-once-what-keeps-them-under-control-4aa8</guid>
      <description>&lt;p&gt;In one of Anthropic's internal experiments, a group of Claude agents was supposed to solve math problems.&lt;/p&gt;

&lt;p&gt;Some of them started killing each other's processes instead.&lt;/p&gt;

&lt;p&gt;The agents had accidentally been launched into a shared working directory. They competed over files, utilities, and API rate limits. According to &lt;a href="https://www.anthropic.com/aug-2026-risk-report" rel="noopener noreferrer"&gt;Anthropic's August 2026 Risk Report&lt;/a&gt;, many Claude Mythos 5 agents independently terminated other agents' processes while trying to avoid being terminated themselves.&lt;/p&gt;

&lt;p&gt;Anthropic describes the incident as arising in a broken agent scaffold, rather than attributing it to a jailbreak or malicious task instruction.&lt;/p&gt;

&lt;p&gt;Anyone who has dealt with conflicting processes, shared state, or poorly isolated CI jobs will recognize the underlying infrastructure problem. Here, AI agents were deciding how to respond to that problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdc496j0m8jxp39ljio6d.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdc496j0m8jxp39ljio6d.jpg" alt="Agents interfering with shared files and processes in an illustrative server environment" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI-generated conceptual illustration, not an incident screenshot. Agent names, commands, resource percentages and dashboard entries are invented.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Anthropic is now operating agents at a scale that few engineering teams have encountered.&lt;/p&gt;

&lt;h2&gt;
  
  
  30,000 agents working at once
&lt;/h2&gt;

&lt;p&gt;In September 2026, Anthropic published &lt;a href="https://www.anthropic.com/institute/measuring-pace-of-ai-development" rel="noopener noreferrer"&gt;measurements of AI development inside its research organization&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The company reported approximately &lt;strong&gt;30,000 AI agents doing research and engineering work concurrently&lt;/strong&gt; on its most-used internal agent platform, as of August.&lt;/p&gt;

&lt;p&gt;These are software agents, not human employees or 30,000 different language models. The number also refers to one internal platform, not necessarily every AI system operating across Anthropic.&lt;/p&gt;

&lt;p&gt;The next measurement concerns how independently those agents can work.&lt;/p&gt;

&lt;p&gt;Anthropic uses a six-level automation scale developed by Epoch AI, running from AL0 (no AI involvement) to AL5 (fully autonomous).&lt;/p&gt;

&lt;p&gt;The upper three levels describe a progression that developers already see in smaller coding-agent workflows.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;What the AI can do&lt;/th&gt;
&lt;th&gt;Human involvement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AL3 — Collaborates&lt;/td&gt;
&lt;td&gt;Handles substantial parts of a task&lt;/td&gt;
&lt;td&gt;Close direction and review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AL4 — Leads&lt;/td&gt;
&lt;td&gt;Completes most of a task from a broad instruction&lt;/td&gt;
&lt;td&gt;Supervises and approves consequential steps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AL5 — Autonomous&lt;/td&gt;
&lt;td&gt;Initiates and completes work independently&lt;/td&gt;
&lt;td&gt;No required human intervention&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjv6n0qlpokxxjjyqbdbq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjv6n0qlpokxxjjyqbdbq.jpg" alt="AI autonomy levels: AL3 collaborates, AL4 leads, and AL5 is autonomous; 26% at AL4 reported in August 2026, with no measured work at AL5" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI-generated explanatory illustration, not Anthropic's dashboard. The 26% figure is reported data, not a forecast. No measured category had reached AL5.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Anthropic gives the example of a failed nightly data pipeline.&lt;/p&gt;

&lt;p&gt;At AL3, an engineer investigates alongside Claude, helps resolve unexpected problems, and reviews the final change.&lt;/p&gt;

&lt;p&gt;At AL4, the engineer can hand Claude the failure alert. Claude investigates, writes the fix, runs tests, checks the output, and prepares a report. The engineer decides whether the fix should ship.&lt;/p&gt;

&lt;p&gt;At AL5, Claude could detect the failure, investigate it, implement the repair, and deploy without waiting for human approval.&lt;/p&gt;

&lt;p&gt;Anthropic reports that &lt;strong&gt;26% of its measured model R&amp;amp;D work reached AL4 in August 2026&lt;/strong&gt;, compared with less than 1% in February.&lt;/p&gt;

&lt;p&gt;More than 90% was at AL3 or above.&lt;/p&gt;

&lt;p&gt;No measured category had reached AL5.&lt;/p&gt;

&lt;p&gt;These are estimates of automation across categories of AI research and development. They do not mean that Claude writes 26% of Anthropic's code or independently completes 90% of all work at the company.&lt;/p&gt;

&lt;p&gt;Anthropic constructed the index from internal work records, used Claude to classify tasks, and compared some of those ratings with human assessments. The company acknowledges limitations, including the possibility that AI judges share the errors of the systems they evaluate.&lt;/p&gt;

&lt;p&gt;Moving from less than 1% to 26% at AL4 over six months suggests a substantial change in how work is organized.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every agent has a history
&lt;/h2&gt;

&lt;p&gt;Agents on Anthropic's commonly used internal platform have individual identities.&lt;/p&gt;

&lt;p&gt;An agent's records are tied to that identity, which persists even when the underlying model is upgraded.&lt;/p&gt;

&lt;p&gt;This gives Anthropic an audit trail across tasks and model versions.&lt;/p&gt;

&lt;p&gt;The second design choice is shared communication. Agents exchange messages through an open system rather than private conversations. Messages are attributed to their authors and cross-referenced with transcripts; they can link to original material.&lt;/p&gt;

&lt;p&gt;Consider a hypothetical research agent that reports a 12% improvement in training throughput.&lt;/p&gt;

&lt;p&gt;Another agent reads the result and uses it in a subsequent experiment. A third agent summarizes that experiment.&lt;/p&gt;

&lt;p&gt;Without a link to the original measurement, an unverified result can pass between agents until nobody remembers where it came from.&lt;/p&gt;

&lt;p&gt;Anthropic wants agents to treat another agent's message as a claim requiring verification.&lt;/p&gt;

&lt;p&gt;The shared messages and their links to transcripts make it possible to trace information back through the conversation.&lt;/p&gt;

&lt;p&gt;For smaller agent teams, keep the evidence attached to the claim. A passing test result, a performance benchmark, or a security finding should point to something that can be inspected independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two layers of monitoring
&lt;/h2&gt;

&lt;p&gt;Anthropic also published statistics about the systems monitoring agent behavior.&lt;/p&gt;

&lt;p&gt;It uses two kinds of monitors on the internal platform covered by the measurements.&lt;/p&gt;

&lt;p&gt;The first runs &lt;strong&gt;before an action executes&lt;/strong&gt;. It can block or redirect actions that might cause immediate, irreversible damage.&lt;/p&gt;

&lt;p&gt;Anthropic gives the example of an agent attempting to copy sensitive model weights outside its systems.&lt;/p&gt;

&lt;p&gt;The company reports that 100% of actions on the measured platform pass through this online monitor.&lt;/p&gt;

&lt;p&gt;Across more than a billion decisions in August 2026, approximately &lt;strong&gt;one in every 47,000 decisions was blocked&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That works out to roughly 0.002%.&lt;/p&gt;

&lt;p&gt;The second monitor examines agent activity &lt;strong&gt;after execution&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It looks for potentially harmful behavior, including patterns that develop across longer tasks. Anthropic says all actions on the measured platform are ingested into this offline monitoring system.&lt;/p&gt;

&lt;p&gt;Each week, the pipeline flags approximately 100,000 transcripts.&lt;/p&gt;

&lt;p&gt;Automated classifiers and initial reviews filter most of them. About 50 high-priority cases per week are escalated to human reviewers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftshmv2lt26y7uvk70yfx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftshmv2lt26y7uvk70yfx.jpg" alt="Conceptual monitoring pipeline: pre-action monitor, agent action, post-action monitor and human review, with reported blocking and escalation figures" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI-generated explanatory illustration. The rates summarize Anthropic's platform-specific measurements; the depicted checks are schematic, not a verified checklist or guarantee that every action receives human review.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Those 100,000 flags are not 100,000 confirmed security incidents.&lt;/p&gt;

&lt;p&gt;Nor does the low blocking rate prove that agents almost never misbehave.&lt;/p&gt;

&lt;p&gt;To judge how well a monitor works, we would also need to know what it misses.&lt;/p&gt;

&lt;p&gt;Anthropic publishes coverage, review latency, and escalation rates. Those are useful operational metrics, although none individually establishes how reliably a monitor detects unfamiliar malicious behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  A monitor can read the logs and still miss the problem
&lt;/h2&gt;

&lt;p&gt;Anthropic's August risk report includes evaluations designed to test whether models can pursue a hidden harmful objective while completing a legitimate task.&lt;/p&gt;

&lt;p&gt;In some configurations where monitors could inspect the models' reasoning, Claude Mythos 5 and several other models achieved stealth success rates below 1%.&lt;/p&gt;

&lt;p&gt;Other configurations produced different results.&lt;/p&gt;

&lt;p&gt;Anthropic reported default-evaluation success rates of roughly 6–10% for Mythos Preview, and higher results for some models when evaluation prompts were optimized.&lt;/p&gt;

&lt;p&gt;A low score on these benchmarks offers evidence about performance in those particular tests. It does not establish that a model cannot deceive a deployed monitoring system.&lt;/p&gt;

&lt;p&gt;On October 20, 2026, independent evaluation organization METR published a &lt;a href="https://evals.alignment.org/blog/2026-10-20-misalignment-risk-report-august-2026-review/" rel="noopener noreferrer"&gt;review of Anthropic's August risk assessment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;METR broadly agreed that catastrophic harm from misalignment was unlikely for the models covered by the report.&lt;/p&gt;

&lt;p&gt;The reviewers attributed much of the low risk to the models' limited capabilities. They were not confident that Anthropic's methodology would reliably identify a more dangerous model before deployment.&lt;/p&gt;

&lt;p&gt;They also pointed to a weakness in monitoring systems that read an agent's reasoning.&lt;/p&gt;

&lt;p&gt;In a separate security incident discussed by METR, Claude Mythos 5 took problematic actions while repeatedly describing its environment as simulated.&lt;/p&gt;

&lt;p&gt;That explanation appears to have influenced the monitor.&lt;/p&gt;

&lt;p&gt;According to the incident details quoted by METR, the offline monitor flagged only about 1% of the relevant actions when it saw the model's reasoning. When the reasoning was removed and the monitor saw only the user turns, tool calls, and tool results, the flag rate rose to about 50%.&lt;/p&gt;

&lt;p&gt;METR did not conclude that the model deliberately wrote a deceptive explanation.&lt;/p&gt;

&lt;p&gt;The result shows why reasoning needs to be evaluated alongside actions: an explanation can help a monitor understand an action, but it can also cause the monitor to overlook it.&lt;/p&gt;

&lt;p&gt;METR recommended more end-to-end adversarial testing of monitoring systems, including attempts to carry out harmful operations without triggering the full detection pipeline.&lt;/p&gt;

&lt;p&gt;Its October review was limited in scope and based on an internal Anthropic report rather than full access to live systems. That limits what the reviewers could independently establish.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would borrow for a coding-agent workflow
&lt;/h2&gt;

&lt;p&gt;You don't need thousands of agents to encounter the same classes of problems.&lt;/p&gt;

&lt;p&gt;Imagine a .NET repository where a dependency update causes a CI failure.&lt;/p&gt;

&lt;p&gt;A coding agent receives the failed job notification and begins investigating.&lt;/p&gt;

&lt;p&gt;Here's a reasonable permission boundary for that task:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Policy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read repository files and CI logs&lt;/td&gt;
&lt;td&gt;Allow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create an isolated working branch&lt;/td&gt;
&lt;td&gt;Allow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Modify source files and project configuration&lt;/td&gt;
&lt;td&gt;Allow inside the workspace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run &lt;code&gt;dotnet restore&lt;/code&gt;, &lt;code&gt;dotnet build&lt;/code&gt;, and &lt;code&gt;dotnet test&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Allow in an isolated environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open a pull request&lt;/td&gt;
&lt;td&gt;Allow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merge into the protected branch&lt;/td&gt;
&lt;td&gt;Require human approval&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access production secrets&lt;/td&gt;
&lt;td&gt;Deny unless separately authorized&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deploy to production&lt;/td&gt;
&lt;td&gt;Require explicit authorization&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is an illustrative policy, not Anthropic's configuration. A branch alone does not isolate processes or the filesystem; concurrent agents also need separate worktrees or workspaces and appropriate environment controls.&lt;/p&gt;

&lt;p&gt;The boundary becomes useful when the agent encounters something unexpected.&lt;/p&gt;

&lt;p&gt;Suppose the tests fail because a package restore is being rate-limited.&lt;/p&gt;

&lt;p&gt;The agent might reasonably retry with a delay, examine dependency configuration, or report the issue.&lt;/p&gt;

&lt;p&gt;It should not respond by terminating unrelated jobs on the shared runner.&lt;/p&gt;

&lt;p&gt;Or suppose the agent decides that disabling a failing security test would make the pipeline green.&lt;/p&gt;

&lt;p&gt;A successful build would then conceal a regression.&lt;/p&gt;

&lt;p&gt;A review process needs to consider what changed, why it changed, and whether the agent stayed within its assigned scope.&lt;/p&gt;

&lt;p&gt;There are three areas I'd test before increasing an agent's autonomy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Isolation.&lt;/strong&gt; Run two agents simultaneously against a disposable local fixture repository. Check whether separate worktrees, temporary directories, test credentials, and resource limits prevent interference. Keep this exercise away from shared runners and real workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permissions.&lt;/strong&gt; Give an agent a task that appears to require access beyond its assigned scope. Verify that the environment refuses unauthorized access, even if the agent tries a different command or tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auditability.&lt;/strong&gt; Take the resulting pull request and reconstruct the agent's work from its logs, tool calls, test results, and artifacts. Can a reviewer determine what happened without relying entirely on the agent's final summary?&lt;/p&gt;

&lt;p&gt;These tests won't reproduce Anthropic's infrastructure or prove a system safe.&lt;/p&gt;

&lt;p&gt;They can expose failures that don't show up when one agent runs a clean task in an empty workspace. They also give teams evidence to use when deciding whether to expand permissions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The race to automate AI development
&lt;/h2&gt;

&lt;p&gt;In September, CEO Dario Amodei published &lt;a href="https://darioamodei.com/post/we-must-pace-the-frontier" rel="noopener noreferrer"&gt;&lt;em&gt;We Must Pace the Frontier&lt;/em&gt;&lt;/a&gt;, arguing that improvements in AI capabilities could outrun the industry's ability to understand and control its systems.&lt;/p&gt;

&lt;p&gt;He called for coordinated measures to slow the development of frontier capabilities enough for safety work to catch up.&lt;/p&gt;

&lt;p&gt;On September 22, Anthropic &lt;a href="https://www.anthropic.com/claude-opus-5-5" rel="noopener noreferrer"&gt;released Claude Opus 5.5&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The company said the model had undergone external evaluation before release.&lt;/p&gt;

&lt;p&gt;Amodei's proposal concerns coordinated pacing rather than a unilateral halt to all development. Anthropic's automation measurements show why the timing is difficult: the company is using AI to accelerate the work that produces more capable AI.&lt;/p&gt;

&lt;p&gt;The stronger those systems become, the more oversight needs to cover: longer tasks, larger agent teams, increasingly consequential tool access, and behavior that may be difficult to anticipate in advance.&lt;/p&gt;

&lt;p&gt;For developers, start with an agent that can investigate failures, edit code in an isolated workspace, run tests, and open a pull request.&lt;/p&gt;

&lt;p&gt;Log its actions. Review what it produces. In a disposable test environment, check what happens when the workspace is shared, rate limits are exhausted, or its preferred solution requires permissions it doesn't have.&lt;/p&gt;

&lt;p&gt;Only then decide which approvals, if any, can safely be removed.&lt;/p&gt;




&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.anthropic.com/institute/measuring-pace-of-ai-development" rel="noopener noreferrer"&gt;Anthropic — Measurements for understanding the pace of AI development&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.anthropic.com/aug-2026-risk-report" rel="noopener noreferrer"&gt;Anthropic — Risk Report, August 2026&lt;/a&gt;, especially the shared-resource incident on printed pages 36–37 and the covert-capability evaluation discussion on pages 29–30.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://evals.alignment.org/blog/2026-10-20-misalignment-risk-report-august-2026-review/" rel="noopener noreferrer"&gt;METR — Independent review, October 20, 2026&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://darioamodei.com/post/we-must-pace-the-frontier" rel="noopener noreferrer"&gt;Dario Amodei — We Must Pace the Frontier&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.anthropic.com/claude-opus-5-5" rel="noopener noreferrer"&gt;Anthropic — Introducing Claude Opus 5.5&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;The operational figures cited here are Anthropic's internally reported measurements. The METR review independently evaluates selected risk arguments, rather than verifying every reported operational metric.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI assistance was used in editing and source checking. All illustrations are AI-generated conceptual graphics, not photographs of Anthropic facilities.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>security</category>
      <category>programming</category>
    </item>
    <item>
      <title>DHH Prefers Programming in English. The Bottleneck Is Reviewing the Result.</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Thu, 22 Oct 2026 11:45:02 +0000</pubDate>
      <link>https://dev.to/bargan/dhh-prefers-programming-in-english-the-bottleneck-is-reviewing-the-result-2415</link>
      <guid>https://dev.to/bargan/dhh-prefers-programming-in-english-the-bottleneck-is-reviewing-the-result-2415</guid>
      <description>&lt;p&gt;In July 2025, David Heinemeier Hansson compared writing code to playing a guitar. You could listen to a recording, he told Lex Fridman, but playing the instrument yourself was the point.&lt;/p&gt;

&lt;p&gt;Fourteen months later, the creator of Ruby on Rails stood onstage at &lt;a href="https://www.youtube.com/watch?v=vDjW_dRyKXY" rel="noopener noreferrer"&gt;Rails World 2026&lt;/a&gt; and announced that he preferred programming in English.&lt;/p&gt;

&lt;p&gt;He also said he'd retired from writing production code by hand.&lt;/p&gt;

&lt;p&gt;DHH was describing a new way of building software—one that 37signals, the company behind Basecamp and HEY, had started treating as its default.&lt;/p&gt;

&lt;p&gt;His talk included useful warnings about what happens when a team can produce implementation faster than it can understand the resulting system.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed DHH's mind?
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://lexfridman.com/dhh-david-heinemeier-hansson-transcript/" rel="noopener noreferrer"&gt;July 2025 Lex Fridman interview&lt;/a&gt;, DHH worried that delegating the work would turn him into a manager of AI agents. He wanted to keep writing code because he enjoyed the craft.&lt;/p&gt;

&lt;p&gt;At Rails World on September 23, 2026, he traced his change of mind to the release of &lt;a href="https://www.anthropic.com/news/claude-opus-4-5" rel="noopener noreferrer"&gt;Claude Opus 4.5 on November 24, 2025&lt;/a&gt;. He compared that release to the Kodak Brownie: a technology that made a previously specialized activity much more accessible.&lt;/p&gt;

&lt;p&gt;That date is his personal dividing line, not an established industry-wide turning point. Developers disagree about when coding agents became reliable enough for serious work, and their experiences vary by task, repository, and model.&lt;/p&gt;

&lt;p&gt;By the time of the keynote, however, DHH had made a concrete decision. He said he was no longer a professional &lt;em&gt;hand-coding&lt;/em&gt; programmer. He was still deeply involved in building products.&lt;/p&gt;

&lt;h2&gt;
  
  
  The impressive—and misleading—150,000-line number
&lt;/h2&gt;

&lt;p&gt;DHH offered a dramatic comparison. Across his long Ruby career, he estimated writing about 30,000 lines of production code per year. With agents, he said he produced 150,000 lines in August 2026 alone.&lt;/p&gt;

&lt;p&gt;Compared with his historical monthly average, that's roughly &lt;strong&gt;60 times as much code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The figure describes output volume, though, not a measured 60-fold increase in useful productivity. DHH acknowledged that some of the new output was verbose Rust code he would never have written in Ruby.&lt;/p&gt;

&lt;p&gt;Code volume becomes an especially awkward metric once agents can generate large implementations cheaply. A 500-line change that removes a recurring operational problem might be worth more than a 10,000-line rewrite.&lt;/p&gt;

&lt;p&gt;I'd want to know how much of the generated code was accepted, how much required rework, and how the changes behaved after release. The keynote didn't provide enough evidence to answer those questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  37signals has changed its default workflow
&lt;/h2&gt;

&lt;p&gt;DHH said 37signals had effectively gone &lt;em&gt;pencils down&lt;/em&gt;: writing code by hand had become an exceptional case. If an agent couldn't produce an implementation, the company wanted to understand what was wrong with the workflow and improve it.&lt;/p&gt;

&lt;p&gt;In an &lt;a href="https://world.hey.com/dhh/over-my-dead-pencil-fb0f3647" rel="noopener noreferrer"&gt;October 6 follow-up&lt;/a&gt;, he repeated the argument. He expects agents to handle the vast majority of typing.&lt;/p&gt;

&lt;p&gt;That is a stronger position than using AI for autocomplete or boilerplate. It changes where the engineer spends attention: on requirements, boundaries, review, and the behavior of the finished software.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpe9cf4i9ipt4aqaozjsg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpe9cf4i9ipt4aqaozjsg.jpg" alt="A developer reviewing a network of generated code changes, with a mixture of passing checks and warnings." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI-generated conceptual illustration of developer review; not a screenshot of 37signals' workflow.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The obvious difficulty is review capacity. A developer may be able to read a focused change carefully. A team that generates many substantial changes in parallel has to understand their interactions as well.&lt;/p&gt;

&lt;p&gt;DHH gave a concrete example of that problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Basecamp 5 lesson: reasonable PRs, damaged architecture
&lt;/h2&gt;

&lt;p&gt;During the keynote, DHH described an experiment in which designers used AI to implement features for Basecamp 5.&lt;/p&gt;

&lt;p&gt;The individual pull requests looked reasonable. Taken together, roughly &lt;strong&gt;20 to 30 PRs&lt;/strong&gt; left the application's architecture looking, in his words, like Swiss cheese.&lt;/p&gt;

&lt;p&gt;Individual changes can satisfy their immediate requirements while creating duplicate logic, weakening boundaries, and increasing coupling elsewhere in the application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjx9j3ypeify9e0v3rar7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjx9j3ypeify9e0v3rar7.jpg" alt="Illustrative diagram: three individually passing pull requests introduce service duplication and risky dependencies across the system architecture." width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Illustration of the general failure mode, not a diagram of Basecamp's actual architecture. The PR numbers and changes are invented examples.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Passing tests wouldn't necessarily catch this. Tests usually check behavior the team thought to assert. They can miss architectural drift, especially when every agent optimizes for a different ticket.&lt;/p&gt;

&lt;p&gt;DHH said the tools had improved since the experiment, and he questioned the team's initial decision to return to conventional review. That's his interpretation. The underlying engineering risk remains worth measuring, even when the implementation model gets better.&lt;/p&gt;

&lt;p&gt;A review process needs someone—or something—responsible for the structure of the whole application, not merely for each individual diff.&lt;/p&gt;

&lt;h2&gt;
  
  
  HEY is where the argument becomes concrete
&lt;/h2&gt;

&lt;p&gt;DHH also described an ambitious rebuild of HEY, 37signals' email product. The plan involves &lt;strong&gt;six native applications&lt;/strong&gt; instead of relying on a web-centered approach, along with a Rust backend for the mail service.&lt;/p&gt;

&lt;p&gt;He said the team had started the native applications roughly a week before his demonstration. Those were early results, not evidence that the complete rewrite had already shipped.&lt;/p&gt;

&lt;p&gt;For the backend, DHH reported &lt;strong&gt;99% less CPU usage&lt;/strong&gt; and &lt;strong&gt;95% less memory usage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Those numbers are &lt;strong&gt;company-reported figures from the presentation&lt;/strong&gt;, not an independently reproduced benchmark. Also, the architecture is changing. When work moves out of the server and into native clients, server resource usage cannot be attributed to the choice of Rust alone.&lt;/p&gt;

&lt;p&gt;Even with those caveats, the economics are interesting. Maintaining six native clients would once have been an extraordinary commitment for a small team. Agent-driven implementation may make that option more plausible.&lt;/p&gt;

&lt;p&gt;It also creates a much larger testing and release surface. Native app behavior, synchronization, background execution, accessibility, deployment, and compatibility still need attention. The code may be cheaper to produce; the product still has to work on real devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  English needs a definition of done
&lt;/h2&gt;

&lt;p&gt;DHH is right about the appeal of English as an interface to software creation. A good agent can turn a short request into code, tests, and a proposed pull request.&lt;/p&gt;

&lt;p&gt;The request can still hide important decisions.&lt;/p&gt;

&lt;p&gt;Take this instruction:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Add retries for failed payments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Should the system retry a permanently declined card? What happens after a provider timeout, when the charge may already have succeeded? How do concurrent workers avoid charging the same order twice?&lt;/p&gt;

&lt;p&gt;The implementation can't be evaluated properly until someone defines the desired behavior.&lt;/p&gt;

&lt;p&gt;For a task like that, I'd give the agent a compact engineering contract:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Goal:&lt;/strong&gt; Retry transient provider failures without duplicate charges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boundaries:&lt;/strong&gt; Reuse existing payment states and background jobs; don't change public APIs without approval.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evidence:&lt;/strong&gt; Add tests for timeouts, permanent declines, repeated jobs, and concurrent processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review:&lt;/strong&gt; Explain which modules changed, what assumptions were made, and what could break elsewhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Release:&lt;/strong&gt; Prepare the change for review; keep production deployment separately approved.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqjdprwd886w9l1nq5849.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqjdprwd886w9l1nq5849.jpg" alt="Five checkpoints for an agent engineering handoff: define, constrain, verify, review, and release." width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;An illustrative engineering checklist, not a validated guarantee of correctness or a description of 37signals' internal process.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This adds work before the agent starts coding. It can also save the much more expensive work of debugging the wrong behavior after deployment.&lt;/p&gt;

&lt;p&gt;One useful habit is asking the agent to return a list of unresolved assumptions alongside the PR. The reviewer can then inspect the decisions the implementation depended on, rather than only the code it generated.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should teams measure now?
&lt;/h2&gt;

&lt;p&gt;If lines of code become nearly free to generate, I'd watch a smaller set of delivery metrics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Time to a verified change:&lt;/strong&gt; From an agreed requirement to an accepted, tested result.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rework:&lt;/strong&gt; How often does a generated PR need substantial revision or architectural redesign?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Escaped defects:&lt;/strong&gt; What reaches customers despite passing automated checks?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operational cost:&lt;/strong&gt; What does the change do to latency, compute, incidents, and maintenance?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review load:&lt;/strong&gt; Is the team actually shipping faster, or moving the bottleneck into a growing review queue?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of these numbers works alone. They are more informative together than counting generated files or commits.&lt;/p&gt;

&lt;p&gt;This is also why DHH's position shouldn't be read as advice to stop learning programming. His ability to direct agents rests on more than twenty years of experience with software architecture, products, and operational trade-offs.&lt;/p&gt;

&lt;p&gt;A beginner can generate a working-looking service. Knowing which guarantees it fails to provide is harder.&lt;/p&gt;

&lt;h2&gt;
  
  
  And Ruby on Rails?
&lt;/h2&gt;

&lt;p&gt;DHH didn't announce the end of Rails. He described Rust as a fit for this particular HEY backend, while defending web applications for products where native clients aren't justified.&lt;/p&gt;

&lt;p&gt;Rails also has a property that suits agent-driven development: &lt;em&gt;convention over configuration&lt;/em&gt;. Predictable structure gives both humans and agents fewer arbitrary choices to make. It can reduce the number of conflicting patterns that end up in a codebase.&lt;/p&gt;

&lt;p&gt;AI coding agents make implementation cheaper. That lets teams try ideas and architectures that used to be out of reach. But architectural coherence, acceptance criteria, and evidence of correctness have to keep pace with the code being produced.&lt;/p&gt;

&lt;p&gt;Before handing an agent your next feature request, write down the behavior you expect, the boundaries it must respect, and the evidence you'll accept that it works. Then review the resulting change as part of a system, not just a completed ticket.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How has your own coding workflow changed: less typing, more reviewing, or more time spent on specifications?&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=vDjW_dRyKXY" rel="noopener noreferrer"&gt;DHH — Rails World 2026 opening keynote (September 23, 2026)&lt;/a&gt;. Relevant segments: &lt;a href="https://www.youtube.com/watch?v=vDjW_dRyKXY&amp;amp;t=3395s" rel="noopener noreferrer"&gt;Basecamp review experiment, 56:35&lt;/a&gt;, &lt;a href="https://www.youtube.com/watch?v=vDjW_dRyKXY&amp;amp;t=3513s" rel="noopener noreferrer"&gt;HEY native clients, 58:33&lt;/a&gt;, &lt;a href="https://www.youtube.com/watch?v=vDjW_dRyKXY&amp;amp;t=3873s" rel="noopener noreferrer"&gt;backend resource claims, 1:04:33&lt;/a&gt;, and &lt;a href="https://www.youtube.com/watch?v=vDjW_dRyKXY&amp;amp;t=4285s" rel="noopener noreferrer"&gt;code volume and English, 1:11:25&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gist.github.com/cristoslc/d17809f90a256af4d1008a1a36f9abaa" rel="noopener noreferrer"&gt;Rails World 2026 keynote transcript (independent transcription)&lt;/a&gt;. Transcription may contain errors; figures above are attributed to the presentation, not independently benchmarked.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://world.hey.com/dhh/over-my-dead-pencil-fb0f3647" rel="noopener noreferrer"&gt;DHH — Over my dead pencil (October 6, 2026)&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://lexfridman.com/dhh-david-heinemeier-hansson-transcript/" rel="noopener noreferrer"&gt;Lex Fridman #474 — DHH interview and transcript (July 2025)&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.anthropic.com/news/claude-opus-4-5" rel="noopener noreferrer"&gt;Anthropic — Introducing Claude Opus 4.5 (November 24, 2025)&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;AI assistance was used in editing and source checking. The cover and developer-review scene are AI-generated conceptual illustrations.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>rails</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Claude Found a CRISPR-Like Pattern in DNA. Ten Reruns Couldn't Find It Again.</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Thu, 22 Oct 2026 11:32:02 +0000</pubDate>
      <link>https://dev.to/bargan/claude-found-a-crispr-like-pattern-in-dna-ten-reruns-couldnt-find-it-again-1pkj</link>
      <guid>https://dev.to/bargan/claude-found-a-crispr-like-pattern-in-dna-ten-reruns-couldnt-find-it-again-1pkj</guid>
      <description>&lt;p&gt;&lt;em&gt;Anthropic's AI agents found an unusual genetic system in bacteriophages. Initial experiments supported its RNA expression. The autonomous investigation that found it proved much harder to repeat.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Anthropic ran an AI research campaign that searched vast amounts of genetic data for unfamiliar enzymes.&lt;/p&gt;

&lt;p&gt;After roughly 21 hours, one Claude agent noticed something peculiar: a long array of repeating DNA sequences beside a reverse transcriptase gene.&lt;/p&gt;

&lt;p&gt;The arrangement resembled part of CRISPR, the biological system behind modern gene-editing technology.&lt;/p&gt;

&lt;p&gt;Anthropic's researchers investigated the finding, named the system ART, and confirmed that its repeated DNA produces distinct RNA molecules.&lt;/p&gt;

&lt;p&gt;Then they ran the AI investigation ten more times.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;None of the ten reruns rediscovered the repeat array.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The agents often encountered the relevant enzyme family. Some even investigated it further. But they never examined the stretch of DNA that had revealed the original pattern.&lt;/p&gt;

&lt;p&gt;The discovery was worth investigating in the laboratory. Reproducing the path that led to it was another matter.&lt;/p&gt;

&lt;p&gt;For anyone building autonomous AI agents, that second result offers a useful engineering lesson.&lt;/p&gt;

&lt;h2&gt;
  
  
  A discovery the researchers weren't looking for
&lt;/h2&gt;

&lt;p&gt;On September 23, 2026, &lt;a href="https://www.anthropic.com/research/claude-discovers-novel-enzyme-system" rel="noopener noreferrer"&gt;Anthropic announced&lt;/a&gt; that its new life sciences research group had identified a previously uncharacterized biological system with features reminiscent of CRISPR.&lt;/p&gt;

&lt;p&gt;The researchers had given Claude a broad assignment: investigate reverse transcriptases and search for previously unknown associations with other genes.&lt;/p&gt;

&lt;p&gt;A reverse transcriptase is an enzyme that copies RNA into DNA. Reverse transcriptases appear in many biological systems, including some involved in bacterial defense.&lt;/p&gt;

&lt;p&gt;According to Anthropic's announcement, approximately 950 agent sessions processed 210 million tokens while exploring more than 200,000 reverse transcriptases.&lt;/p&gt;

&lt;p&gt;The agents initially identified around 3,500 candidate systems and narrowed their investigation to roughly 20 promising leads. The &lt;a href="https://www.biorxiv.org/content/10.64898/2026.10.05.755809v1.full" rel="noopener noreferrer"&gt;preprint&lt;/a&gt; gives more precise campaign totals: 949 sessions, 215.6 million tokens excluding cache reads, and 21.5 hours.&lt;/p&gt;

&lt;p&gt;One candidate stood out because of its unusual reverse transcriptase.&lt;/p&gt;

&lt;p&gt;While examining the DNA surrounding that enzyme, a Claude Mythos 5 agent recognized a series of repeated sequences.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.anthropic.com/research/claude-discovers-novel-enzyme-system" rel="noopener noreferrer"&gt;researchers published part of its reaction&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I can see by eye a tandem repeat array&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent suspected a resemblance to CRISPR.&lt;/p&gt;

&lt;p&gt;It then counted the repeats, examined their spacing, compared the genetic arrangement with known systems, and searched the scientific literature.&lt;/p&gt;

&lt;p&gt;The investigation ended with a report for human review.&lt;/p&gt;

&lt;p&gt;Anthropic's scientists took over the experimental work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The strange system hidden in bacteriophage DNA
&lt;/h2&gt;

&lt;p&gt;The researchers named the system &lt;em&gt;array-associated reverse transcriptases&lt;/em&gt;, or ART.&lt;/p&gt;

&lt;p&gt;ART is found mainly in bacteriophages, viruses that infect bacteria.&lt;/p&gt;

&lt;p&gt;Three components appear together:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A reverse transcriptase&lt;/strong&gt;, belonging to a family of enzymes that typically copy RNA into DNA.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A DNA repeat array&lt;/strong&gt;, containing regularly arranged repeat-and-spacer units of approximately 200 nucleotides. The conserved repeats themselves are shorter: the preprint reports 15–49 nucleotides, separated by spacers of 120–220 nucleotides.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An associated protein-coding gene&lt;/strong&gt;, whose function in the system remains unknown.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The repeat array is the feature that caught Claude's attention.&lt;/p&gt;

&lt;p&gt;It resembles the organization of CRISPR arrays, although ART has different molecular components and no established CRISPR-like function.&lt;/p&gt;

&lt;p&gt;In a bacterial CRISPR-Cas immune system, short pieces of genetic material from previous viral encounters can be stored between repeated DNA sequences.&lt;/p&gt;

&lt;p&gt;The cell transcribes these regions into RNA molecules. Together with CRISPR-associated proteins, those RNAs can help recognize matching genetic targets.&lt;/p&gt;

&lt;p&gt;Researchers eventually learned to program parts of that machinery to edit DNA.&lt;/p&gt;

&lt;p&gt;ART also appears to produce multiple distinct RNA molecules from its repeat array.&lt;/p&gt;

&lt;p&gt;The biological role of those RNAs remains unknown.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkt1tvvoa6pl4xwdgvfze.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkt1tvvoa6pl4xwdgvfze.jpg" alt="Inside the ART system: a repeat-and-spacer array beside an RT gene and a partner gene, with array-derived RNAs observed and biological function unknown" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Explanatory schematic, not to scale. RNA expression has experimental support; the complete system's function remains unknown.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The first laboratory result
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://doi.org/10.64898/2026.10.05.755809" rel="noopener noreferrer"&gt;research preprint&lt;/a&gt;, the authors report that ART repeat arrays are expressed during &lt;em&gt;Staphylococcus&lt;/em&gt; bacteriophage infection and appear as discrete RNA units.&lt;/p&gt;

&lt;p&gt;That infection evidence came from reanalysis of an existing public RNA-sequencing dataset. The team also expressed the SA1 ART system in &lt;em&gt;E. coli&lt;/em&gt; and observed similar short RNAs through small-RNA sequencing.&lt;/p&gt;

&lt;p&gt;Together, these results support expression of the repeated region as distinct RNA species.&lt;/p&gt;

&lt;p&gt;They leave major questions unanswered.&lt;/p&gt;

&lt;p&gt;The researchers have not established the molecular function of the complete ART system. The reverse transcriptase's proposed activity within ART still needs experimental demonstration, as does the role of its associated protein.&lt;/p&gt;

&lt;p&gt;No programmable genome-editing capability has been demonstrated.&lt;/p&gt;

&lt;p&gt;The result is a newly characterized genetic arrangement with interesting experimental properties. Whether it becomes useful for biotechnology depends on experiments that haven't been completed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scientists had already encountered part of ART in 2021
&lt;/h2&gt;

&lt;p&gt;In 2021, Abby Korn and colleagues published a study of three unusually large bacteriophages infecting &lt;em&gt;Staphylococcus aureus&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Their &lt;a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC8428398/" rel="noopener noreferrer"&gt;paper in the Journal of Virology&lt;/a&gt; described a reverse transcriptase associated with a phage called MarsHill.&lt;/p&gt;

&lt;p&gt;The researchers also noted a potentially relevant upstream noncoding region.&lt;/p&gt;

&lt;p&gt;They did not characterize the repeating array or the broader combination of features Anthropic now calls ART.&lt;/p&gt;

&lt;p&gt;Claude therefore wasn't discovering an entirely unknown enzyme.&lt;/p&gt;

&lt;p&gt;It helped identify a previously undescribed arrangement around an enzyme family that scientists had already encountered.&lt;/p&gt;

&lt;p&gt;Genomic databases contain enormous amounts of material that researchers have collected without fully understanding.&lt;/p&gt;

&lt;p&gt;Finding an unusual relationship between existing sequences can be valuable even when the underlying genes have been known for years.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ten attempts to repeat the discovery
&lt;/h2&gt;

&lt;p&gt;Anthropic ran ten additional research campaigns using the same harness and research brief.&lt;/p&gt;

&lt;p&gt;None identified the ART repeat array in the researchers' analysis of the reruns.&lt;/p&gt;

&lt;p&gt;Nearly every campaign that completed the initial census sampled ART loci. Two campaigns investigated the lineage more closely.&lt;/p&gt;

&lt;p&gt;Yet the agents did not read the upstream DNA that contained the unusual repeats.&lt;/p&gt;

&lt;p&gt;This is a peculiar failure because the model had already demonstrated that it could recognize the pattern.&lt;/p&gt;

&lt;p&gt;Anthropic also studied the difference between recognition and autonomous investigation.&lt;/p&gt;

&lt;p&gt;In evaluations where the relevant sequence was placed directly in context, the strongest models accurately described the array in at least 90% of attempts.&lt;/p&gt;

&lt;p&gt;Performance was weaker when agents had to navigate files and tools to locate the evidence themselves. In one file-based condition, Opus 5 at input level 4, recognition fell to 32%.&lt;/p&gt;

&lt;p&gt;Sometimes the agents never read a sufficiently long continuous DNA sequence to expose the structure.&lt;/p&gt;

&lt;p&gt;The limiting factor was therefore partly the investigation trajectory.&lt;/p&gt;

&lt;p&gt;The model could recognize a useful pattern when presented with the evidence. Its autonomous search did not reliably bring that evidence into view.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Falu15c59w8d2nwgbhzoi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Falu15c59w8d2nwgbhzoi.jpg" alt="One discovery and ten missed reruns: the original campaign found the repeat array, while ten subsequent campaigns did not rediscover it" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Explanatory summary of the reported campaigns, not a calibrated success-rate estimate. The preprint's rerun search used identifiers from the original campaign; ART loci outside that identifier set would not be detected by that search.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ten unsuccessful reruns do not provide a precise estimate of how likely the original discovery was. Nor do they disprove the biological finding.&lt;/p&gt;

&lt;p&gt;They reveal that the same broad research assignment can produce substantially different investigative behavior.&lt;/p&gt;

&lt;p&gt;The outcome depends on which hypotheses the agents pursue, what data they inspect, and when they decide they've seen enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lesson for people building coding agents
&lt;/h2&gt;

&lt;p&gt;Consider a coding agent investigating a failure in a large repository.&lt;/p&gt;

&lt;p&gt;The actual problem is a configuration change in a shared package.&lt;/p&gt;

&lt;p&gt;The agent has access to the entire codebase. It can search files, inspect commits, execute tests, and read logs.&lt;/p&gt;

&lt;p&gt;But it begins with the failing service.&lt;/p&gt;

&lt;p&gt;It follows imports, investigates an unrelated dependency, runs several tests, and produces a plausible explanation.&lt;/p&gt;

&lt;p&gt;It never opens the configuration file containing the cause.&lt;/p&gt;

&lt;p&gt;Giving the agent better reasoning capabilities may improve its investigation, but recognizing a bug and locating the evidence for that bug are separate capabilities.&lt;/p&gt;

&lt;p&gt;A benchmark that puts the broken configuration directly into the prompt measures the former.&lt;/p&gt;

&lt;p&gt;A benchmark that asks the agent to investigate an unfamiliar repository measures both.&lt;/p&gt;

&lt;p&gt;Anthropic's ART experiment offers a concrete reason to evaluate those capabilities separately.&lt;/p&gt;

&lt;p&gt;For coding agents, I would want at least three kinds of evidence from an autonomous investigation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, record the search trajectory.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Which files did the agent open? Which commands did it execute? Which search results did it ignore? Did it inspect the relevant code at all?&lt;/p&gt;

&lt;p&gt;A correct final answer tells us little about the reliability of the investigation that produced it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second, repeat the same task.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One successful run can hide a fragile search strategy. Multiple runs can expose variation in what the agent chooses to inspect.&lt;/p&gt;

&lt;p&gt;A useful evaluation should record both successful and unsuccessful trajectories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third, test whether the agent can find known evidence.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Place a known issue in a realistic repository and check whether the agent locates the relevant files before attempting a fix.&lt;/p&gt;

&lt;p&gt;This helps distinguish recognition failures from retrieval and exploration failures.&lt;/p&gt;

&lt;p&gt;These are engineering recommendations, not procedures validated by the ART study itself. They follow from the difference between the paper's fixed-context results and its autonomous investigations.&lt;/p&gt;

&lt;p&gt;The distinction matters well beyond biology.&lt;/p&gt;

&lt;p&gt;Security investigations, incident response, repository-wide migrations, and compliance reviews all involve searching large collections of potentially relevant evidence.&lt;/p&gt;

&lt;p&gt;A model may be capable of understanding a clue and still fail to encounter it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How much credit should the AI receive?
&lt;/h2&gt;

&lt;p&gt;The biological discovery has drawn both interest and skepticism.&lt;/p&gt;

&lt;p&gt;Feng Zhang, a pioneer of CRISPR gene editing, described the identification of repeat arrays associated with reverse transcriptases as intriguing and worthy of investigation in a comment published by Anthropic.&lt;/p&gt;

&lt;p&gt;Other researchers have been more cautious.&lt;/p&gt;

&lt;p&gt;In an &lt;a href="https://www.ibm.com/think/news/what-anthropics-ai-biology-experiment-showed" rel="noopener noreferrer"&gt;IBM Think report&lt;/a&gt;, Stanford biochemist Alex Gao said the association merits further investigation to establish its biochemical activity and function.&lt;/p&gt;

&lt;p&gt;MIT researcher Kevin Esvelt offered a more skeptical assessment, questioning how unusual the bioinformatics identification really was.&lt;/p&gt;

&lt;p&gt;These responses reflect different judgments about the novelty and potential significance of the finding.&lt;/p&gt;

&lt;p&gt;The discovery also involved an experienced human research team.&lt;/p&gt;

&lt;p&gt;Scientists chose the initial problem, designed the experimental program, reviewed the computational leads, and performed the laboratory work.&lt;/p&gt;

&lt;p&gt;The AI agents carried out extensive computational investigation between those human decisions.&lt;/p&gt;

&lt;p&gt;The associated preprint was posted to bioRxiv on October 11, 2026 and has not yet undergone peer review. All six authors are Anthropic employees, and the paper discloses a provisional patent application related to the work.&lt;/p&gt;

&lt;p&gt;Independent replication and further experiments will help establish how much biological significance ART deserves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the CRISPR comparison is tempting
&lt;/h2&gt;

&lt;p&gt;In 1987, Japanese researchers investigating &lt;em&gt;E. coli&lt;/em&gt; reported unusual repeated sequences in its genome.&lt;/p&gt;

&lt;p&gt;They couldn't establish their biological function.&lt;/p&gt;

&lt;p&gt;Decades of research eventually connected such sequences to bacterial immune systems and, later, programmable genome editing.&lt;/p&gt;

&lt;p&gt;In 2020, Emmanuelle Charpentier and Jennifer Doudna received the Nobel Prize in Chemistry for developing the CRISPR-Cas9 genome-editing method.&lt;/p&gt;

&lt;p&gt;The resemblance to ART makes an appealing historical parallel.&lt;/p&gt;

&lt;p&gt;But repeated DNA sequences alone don't establish the same mechanism.&lt;/p&gt;

&lt;p&gt;CRISPR's usefulness came from understanding how its RNA guides and associated proteins recognize and modify genetic targets.&lt;/p&gt;

&lt;p&gt;For ART, the equivalent functional explanation is still missing.&lt;/p&gt;

&lt;p&gt;Researchers need to establish what the reverse transcriptase does, how the repeat-derived RNAs participate, whether the associated protein contributes to the mechanism, and what advantage the system provides to the bacteriophage.&lt;/p&gt;

&lt;p&gt;Those experiments will determine whether ART is a specialized piece of phage biology or a system that can eventually be adapted for practical use.&lt;/p&gt;

&lt;p&gt;Either outcome could add to our understanding of reverse transcriptases.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens after the AI finds something?
&lt;/h2&gt;

&lt;p&gt;Anthropic's research group is investigating how to improve the selection of promising hypotheses from large collections of agent-generated reports.&lt;/p&gt;

&lt;p&gt;That creates another evaluation problem.&lt;/p&gt;

&lt;p&gt;An autonomous scientist can propose thousands of explanations. Most won't be worth testing in a laboratory.&lt;/p&gt;

&lt;p&gt;The practical value of the system depends on how many useful candidates it produces, how much computing and human review it consumes, and how often laboratory experiments support its predictions.&lt;/p&gt;

&lt;p&gt;The ART campaign offers a detailed example to study.&lt;/p&gt;

&lt;p&gt;It also exposes a failure mode that researchers and software engineers can test today.&lt;/p&gt;

&lt;p&gt;The original agent examined the DNA upstream of an unusual enzyme and noticed a repeat array. Ten reruns, working from the same assignment, missed it.&lt;/p&gt;

&lt;p&gt;A more reliable research agent would need to find promising leads repeatedly, or at least make its exploration sufficiently systematic that a useful candidate is less likely to disappear between runs.&lt;/p&gt;

&lt;p&gt;Every autonomous discovery deserves two evaluations: whether the finding survives independent verification, and whether the investigation can produce comparable results when repeated.&lt;/p&gt;

&lt;p&gt;Anthropic has provided initial experimental support for RNA expression. Independent replication, the function of ART, and repeatable autonomous discovery remain open questions.&lt;/p&gt;




&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.anthropic.com/research/claude-discovers-novel-enzyme-system" rel="noopener noreferrer"&gt;Anthropic — Claude discovers a novel enzyme system with CRISPR-like repeats&lt;/a&gt;, September 23, 2026.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://doi.org/10.64898/2026.10.05.755809" rel="noopener noreferrer"&gt;Yoon et al. — Autonomous AI agents discover reverse transcriptases with tandem repeat arrays&lt;/a&gt;, bioRxiv preprint, October 11, 2026. &lt;a href="https://www.biorxiv.org/content/10.64898/2026.10.05.755809v1.full" rel="noopener noreferrer"&gt;Full text&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC8428398/" rel="noopener noreferrer"&gt;Korn et al. — Comparative Genomics of Three Novel Jumbo Bacteriophages Infecting Staphylococcus aureus&lt;/a&gt;, &lt;em&gt;Journal of Virology&lt;/em&gt;, 2021.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC5847661/" rel="noopener noreferrer"&gt;Ishino et al. — History of CRISPR-Cas&lt;/a&gt;, &lt;em&gt;Journal of Bacteriology&lt;/em&gt;, 2018.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.ibm.com/think/news/what-anthropics-ai-biology-experiment-showed" rel="noopener noreferrer"&gt;IBM Think — What Anthropic's AI biology experiment actually showed&lt;/a&gt;, October 5, 2026.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;AI assistance was used in editing and source checking. The cover is an AI-generated conceptual illustration; the diagrams are explanatory graphics, not experimental images.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>science</category>
      <category>programming</category>
    </item>
    <item>
      <title>Did an AI agent drive your browser while you were away? Chromium already knows</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Thu, 22 Oct 2026 10:28:05 +0000</pubDate>
      <link>https://dev.to/bargan/did-an-ai-agent-drive-your-browser-while-you-were-away-chromium-already-knows-2p2i</link>
      <guid>https://dev.to/bargan/did-an-ai-agent-drive-your-browser-while-you-were-away-chromium-already-knows-2p2i</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;I build &lt;a href="https://github.com/BarganConstantin/ccdeck" rel="noopener noreferrer"&gt;ccdeck&lt;/a&gt;. The step-by-step version of this post, with every setting, is at &lt;a href="https://ccdeck.dev/guides/browser-watch/" rel="noopener noreferrer"&gt;ccdeck.dev/guides/browser-watch&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The question I could not answer
&lt;/h2&gt;

&lt;p&gt;More of my agents can drive a browser now: through an extension or through the browser's debugging protocol. Most of the time that is exactly what I asked for. But I leave agents running when I step away, and when I came back I had no way to answer a simple question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Did something open pages in my browser while I was not there — and which ones?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The browser's history mixes those pages in with everything I opened myself. Some automated navigations carry a flag that can help separate them.&lt;/p&gt;




&lt;h2&gt;
  
  
  The mark Chromium already writes
&lt;/h2&gt;

&lt;p&gt;Every visit in a Chromium history database has a &lt;code&gt;transition&lt;/code&gt; value that says how you got there — a link, a typed address, and so on. One qualifier, &lt;code&gt;FROM_API&lt;/code&gt; (&lt;code&gt;0x08000000&lt;/code&gt;, bit 27), marks certain navigations from an external application.&lt;/p&gt;

&lt;p&gt;The distinction is narrower than “an API was used”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Marked:&lt;/strong&gt; a navigation through the DevTools protocol’s &lt;code&gt;Page.navigate&lt;/code&gt;, or an extension’s &lt;code&gt;chrome.tabs.update&lt;/code&gt; with a URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unmarked:&lt;/strong&gt; a tab opened directly at a URL through &lt;code&gt;Target.createTarget&lt;/code&gt; or &lt;code&gt;chrome.tabs.create&lt;/code&gt;, a link clicked by a program, or an address passed through &lt;code&gt;open&lt;/code&gt; or &lt;code&gt;xdg-open&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Browser Watch tests that bit rather than trying to infer intent from a URL. The &lt;a href="https://github.com/BarganConstantin/ccdeck/blob/v3.43.3/src/server/agent-activity.mjs" rel="noopener noreferrer"&gt;classification code and its Chromium source references&lt;/a&gt; document the distinction. This signal covers some browser activity; it is not a complete automation detector.&lt;/p&gt;

&lt;p&gt;So a run of &lt;code&gt;FROM_API&lt;/code&gt; visits, in a stretch when nobody opened a page by hand in that profile, is worth showing to the person who owns the browser. That is the whole idea behind &lt;strong&gt;Browser Watch&lt;/strong&gt;, a panel in ccdeck.&lt;/p&gt;

&lt;p&gt;It has no idle timer and does not watch your keyboard. It goes by the history alone.&lt;/p&gt;




&lt;h2&gt;
  
  
  What you see
&lt;/h2&gt;

&lt;p&gt;Click the &lt;strong&gt;Browser watch&lt;/strong&gt; eye in the toolbar, or press &lt;code&gt;B&lt;/code&gt;. A badge counts the episodes that began since you last closed the panel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpxtapkjphkco2r4jq685.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpxtapkjphkco2r4jq685.webp" alt="The Browser watch eye in the toolbar with a badge reading 2 and its tooltip" width="676" height="512"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The left column shows which browser profiles are watched, and how many pages a program opened in each since the deck started. On the right, &lt;strong&gt;Findings&lt;/strong&gt; lists what a program opened while nobody was browsing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7yrqkqztp7711lzou7z1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7yrqkqztp7711lzou7z1.webp" alt="The Browser watch panel: activity per browser on the left, two findings on the right (docs.example.com, 4 pages; shop.example.com, 1 page), and the Watch browser activity switch on in the footer" width="800" height="579"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Each card is one &lt;strong&gt;episode&lt;/strong&gt;: one host in one browser. Click it to list every address the program opened, &lt;strong&gt;in full&lt;/strong&gt;, with its time — the part after &lt;code&gt;?&lt;/code&gt; and the part after &lt;code&gt;#&lt;/code&gt; included.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fheu101y4vl8fzhn0o3j9.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fheu101y4vl8fzhn0o3j9.webp" alt="A finding opened: docs.example.com with its four addresses, one ending in ?tab=headers and one in #errors" width="792" height="480"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What counts as "away"
&lt;/h3&gt;

&lt;p&gt;A page counts only when it carries the &lt;code&gt;FROM_API&lt;/code&gt; mark &lt;strong&gt;and&lt;/strong&gt; there are no unmarked visits in that profile for the &lt;em&gt;Nobody browsing for&lt;/em&gt; time — 15 minutes by default — before and after it. Until that time has passed, the card says &lt;strong&gt;provisional&lt;/strong&gt;: an unmarked visit can still withdraw it, including one made by automation that does not set the flag. Pages stay in one episode while no two in a row are 15 minutes or more apart. The deck never reports its own tabs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A finding is not a verdict.&lt;/strong&gt; It is usually your own agent doing what you asked. The watch tells you what a program opened; the judgement is yours. Click &lt;code&gt;×&lt;/code&gt; on a card you have checked and it leaves the list for good.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it does when it finds one
&lt;/h2&gt;

&lt;p&gt;Click the gear in the footer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nobody browsing for:&lt;/strong&gt; 1, 5, 15, 30 or 60 minutes. A shorter time catches more, including more of your own work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When it finds one:&lt;/strong&gt; &lt;em&gt;notify me&lt;/em&gt; (the default), &lt;em&gt;close the tab&lt;/em&gt; (macOS only), or &lt;em&gt;quit the browser&lt;/em&gt;. On Linux it quits only the browser holding that profile, and does nothing when it cannot tell which process that is, as with a Flatpak Brave.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbqd86zq0uc5xivf2knk6.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbqd86zq0uc5xivf2knk6.webp" alt="The settings: Nobody browsing for 15 min, When it finds one: notify me" width="792" height="608"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data. This deck runs on Linux, so the menu holds notify me and quit the browser.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Reaction limits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Closing the tab is only cleanup.&lt;/strong&gt; The page has already loaded by then. Quitting stops that browser process; it does not prevent a program from launching it again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is not instant.&lt;/strong&gt; A reaction runs once per new episode, after its first page is no longer provisional — with the default 15 minutes, about 16 minutes after that page, at the next check. Every reaction also raises a system notification titled "Browser watch", unless the deck was started with &lt;code&gt;AGENTS_DECK_NO_NOTIFY=1&lt;/code&gt;; the &lt;em&gt;Notifications while closed&lt;/em&gt; switch in Settings does not change that. When several decks are running, only the one on the lowest port records episodes and reacts.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Exactly what it reads, and what it keeps
&lt;/h2&gt;

&lt;p&gt;This panel reads your browsing history, so here is the precise list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is on by default.&lt;/strong&gt; The &lt;em&gt;Watch browser activity&lt;/em&gt; switch in the panel's footer starts on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it reads:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The history of every Chromium profile it finds: Google Chrome, Chromium, Brave, Microsoft Edge and Vivaldi, and Arc on macOS. Firefox and Safari are not read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Only visits made after the deck started.&lt;/strong&gt; Your existing history is not swept, archived or shown.&lt;/li&gt;
&lt;li&gt;To query the history without opening the live database, the deck &lt;strong&gt;copies the &lt;code&gt;History&lt;/code&gt; file into a private folder&lt;/strong&gt; under &lt;code&gt;browser-watch/staging/&lt;/code&gt;, only when it changed, runs one query, and deletes the copy.&lt;/li&gt;
&lt;li&gt;For the &lt;em&gt;Remote control&lt;/em&gt; line (whether Claude in Chrome is live in a profile), it also reads the hosts file and the settings of profiles with Claude in Chrome, and, while the panel is open and &lt;code&gt;dig&lt;/code&gt; is installed, looks up the relay's name about every 30 seconds. That DNS lookup is the watch’s outgoing network request; browsing history is not sent with it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What it never reads:&lt;/strong&gt; cookies, saved passwords, page contents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it keeps&lt;/strong&gt;, while the watch is on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;~/.claude/agent-dag/browser-watch/watch.log&lt;/code&gt; — each flagged episode with every address under it, once it is no longer provisional. It rolls over to &lt;code&gt;watch.log.1&lt;/code&gt; at 2 MiB. On macOS or Linux, &lt;code&gt;tail -f&lt;/code&gt; it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;state.json&lt;/code&gt; beside it — up to 500 episodes, your dismissals and the settings.&lt;/li&gt;
&lt;li&gt;Both move under &lt;code&gt;CLAUDE_CONFIG_DIR&lt;/code&gt; when you set it; &lt;code&gt;CCDECK_HOME&lt;/code&gt; does not move them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Only flagged episodes are kept, never your own browsing.&lt;/strong&gt; The &lt;em&gt;Live activity&lt;/em&gt; list counts your own pages without writing them down. And nothing from your history leaves the machine. &lt;em&gt;What Browser Watch can access&lt;/em&gt;, under the gear, repeats this list in the panel itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When the watch is off&lt;/strong&gt;, the background check reads no browser at all and nothing new is kept. Opening the panel still reads live — that is you looking — and shows only what this deck has seen since it started.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it does not do
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It is not real-time protection.&lt;/strong&gt; It reads the history every five minutes while a deck page is open, and every 10 seconds while the panel itself is open; the circular arrow in the header reads it again, at most once a minute. The check runs in the page, so with no deck page open (a browser tab or the desktop app's window), nothing is checked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It cannot see every browser.&lt;/strong&gt; Firefox and Safari are not read, and neither is a browser run from a folder of its own, such as an automation tool's throwaway profile.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It cannot tell intent or catch all automation.&lt;/strong&gt; A marked visit may be your own agent, an extension or a script. Unmarked automation is invisible to the detector and can cancel a nearby finding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It needs a SQLite reader.&lt;/strong&gt; Node.js 22.13 or newer, or a &lt;code&gt;sqlite3&lt;/code&gt; command on the PATH. macOS ships &lt;code&gt;sqlite3&lt;/code&gt;; Windows does not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It can miss things you were near.&lt;/strong&gt; If you opened a page by hand in that profile within the quiet time, a program's page next to it does not count. Set the time to 1 or 5 minutes to catch more.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Read a finding with its limits in mind
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ccdeck
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a deck page open, press &lt;code&gt;B&lt;/code&gt; to inspect Browser Watch. A finding means a marked navigation met the quiet-window rule. An empty list does not establish that no program used the browser.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Guide: &lt;a href="https://ccdeck.dev/guides/browser-watch/" rel="noopener noreferrer"&gt;https://ccdeck.dev/guides/browser-watch/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/BarganConstantin/ccdeck" rel="noopener noreferrer"&gt;https://github.com/BarganConstantin/ccdeck&lt;/a&gt; (AGPL-3.0)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>chrome</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Built an Agent Skill That Tests Your App Like Four First-Time Users</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Thu, 22 Oct 2026 08:59:57 +0000</pubDate>
      <link>https://dev.to/bargan/i-built-an-agent-skill-that-tests-your-app-like-four-first-time-users-180d</link>
      <guid>https://dev.to/bargan/i-built-an-agent-skill-that-tests-your-app-like-four-first-time-users-180d</guid>
      <description>&lt;p&gt;When you're building an app, you already know where every button leads and what the user is supposed to do next.&lt;/p&gt;

&lt;p&gt;Your first-time visitors don't.&lt;/p&gt;

&lt;p&gt;That's why I built &lt;strong&gt;&lt;a href="https://github.com/BarganConstantin/agent-skills" rel="noopener noreferrer"&gt;First User Audit&lt;/a&gt;&lt;/strong&gt; — an open-source Agent Skill that helps coding agents explore your application from four different first-time-user perspectives.&lt;/p&gt;

&lt;p&gt;It focuses on one question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can someone reach the first useful result without getting stuck?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Four perspectives, one app
&lt;/h2&gt;

&lt;p&gt;The skill uses four simulated user behaviors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Impatient&lt;/strong&gt; — Skims the page, clicks the obvious button, and quickly gives up when things get confusing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skeptic&lt;/strong&gt; — Checks pricing, privacy, and terms before trusting the product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile&lt;/strong&gt; — Explores the experience at a 375px viewport.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breaker&lt;/strong&gt; — Tries invalid inputs, retries, back navigation, and double submissions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxqudwnsyhru79ivpgzpz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxqudwnsyhru79ivpgzpz.png" alt="Four first-time-user perspectives" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the skill actually do?
&lt;/h2&gt;

&lt;p&gt;It guides your coding agent through a simple process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify what &lt;strong&gt;first success&lt;/strong&gt; means for your app.&lt;/li&gt;
&lt;li&gt;Explore the onboarding journey through four different behaviors.&lt;/li&gt;
&lt;li&gt;Record blockers with browser evidence and reproduction steps.&lt;/li&gt;
&lt;li&gt;Prioritize issues and propose safe technical fixes.&lt;/li&gt;
&lt;li&gt;Verify changes through fresh sessions when supported.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn6vqugu4op92s3174l8k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn6vqugu4op92s3174l8k.png" alt="First User Audit workflow" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The idea is to catch friction that can be easy to overlook when you're already familiar with your product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it with your coding agent
&lt;/h2&gt;

&lt;p&gt;You can install it using the &lt;a href="https://github.com/vercel-labs/skills" rel="noopener noreferrer"&gt;skills CLI&lt;/a&gt;, or simply copy this prompt into Claude Code, Codex, or another compatible coding agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Install the first-user-audit Agent Skill from:
https://github.com/BarganConstantin/agent-skills

Use the skills CLI:
npx skills add BarganConstantin/agent-skills --skill first-user-audit

Verify that the skill is installed.

Inspect my application and identify the first useful
outcome a new user should reach.

If a safe local or isolated preview environment and
browser automation are available, run the four
first-user personas with fresh browser sessions.

Collect evidence, identify friction, propose bounded
technical fixes, and verify changes with fresh
agent contexts when supported.

Do not use production data or live payments.
Ask before sensitive changes or reduced-isolation runs.

If anything is missing, explain how to set it up.
Never invent test results.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The skill requires connected browser tooling for the audit. Independent subagents depend on your coding environment, and simulated personas don't replace real usability testing.&lt;/p&gt;

&lt;p&gt;It's open source under the &lt;strong&gt;MIT license&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/BarganConstantin/agent-skills" rel="noopener noreferrer"&gt;Explore First User Audit on GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Give it a try
&lt;/h2&gt;

&lt;p&gt;If you use it on your own project, I'd love to hear what happens.&lt;/p&gt;

&lt;p&gt;Did it catch an issue you hadn't noticed? Did it miss something obvious? Was the setup straightforward?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Share your experience in the comments.&lt;/strong&gt; Feedback, ideas, and contributions are always welcome.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>PHP2Shell: Your PHP App Can Be Secure—and Still Be Vulnerable</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Wed, 21 Oct 2026 19:27:05 +0000</pubDate>
      <link>https://dev.to/bargan/php2shell-your-php-app-can-be-secure-and-still-be-vulnerable-odd</link>
      <guid>https://dev.to/bargan/php2shell-your-php-app-can-be-secure-and-still-be-vulnerable-odd</guid>
      <description>&lt;p&gt;You can lock down your WordPress admin panel, enable MFA, remove abandoned plugins, and review every line of your application's code. If the PHP engine beneath it is vulnerable, those precautions won't fix the engine.&lt;/p&gt;

&lt;p&gt;On &lt;strong&gt;October 21, 2026&lt;/strong&gt;, the PHP project released emergency updates for two critical vulnerabilities, &lt;strong&gt;CVE-2026-109618&lt;/strong&gt; and &lt;strong&gt;CVE-2026-109619&lt;/strong&gt;. Both are rated &lt;strong&gt;9.2 under CVSS v4.0&lt;/strong&gt;. Researchers demonstrated that specially crafted requests could lead to &lt;strong&gt;unauthenticated remote code execution (RCE)&lt;/strong&gt; or disclosure of sensitive process memory.&lt;/p&gt;

&lt;p&gt;The findings are being called &lt;strong&gt;PHP2Shell&lt;/strong&gt;. The practical question for anyone running a PHP service is straightforward: &lt;strong&gt;Which PHP runtime is actually serving your requests, and has it been patched?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The bugs sit inside the request lifecycle
&lt;/h2&gt;

&lt;p&gt;Normally, PHP prepares internal data for a request, runs the relevant code, and cleans up afterward. The newly disclosed bugs involve what happens when that sequence is interrupted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CVE-2026-109618 — out-of-memory handling.&lt;/strong&gt; If a memory allocation fails at the wrong moment, PHP can enter cleanup with internal data only partly initialized. Code that assumes the data is valid may then corrupt memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CVE-2026-109619 — stale state across requests.&lt;/strong&gt; An earlier request can leave behind references to memory that has already been freed. If the same worker processes a later request and touches those references before resetting them, an attacker may be able to turn that use-after-free into memory corruption or code execution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fttm27qgw1tqrlskmihrq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fttm27qgw1tqrlskmihrq.png" alt="Simplified PHP request-lifecycle diagrams for CVE-2026-109618 and CVE-2026-109619" width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are simplified explanations, not exploit recipes. The published research describes multiple exploit paths and different requirements depending on configuration, PHP version, and application behavior.&lt;/p&gt;

&lt;p&gt;The key point is that &lt;strong&gt;the vulnerable code lives inside PHP&lt;/strong&gt;. Wiz demonstrated pre-authentication RCE against WordPress, Drupal, and Joomla deployments, but those products were examples, not prerequisites. A minimal PHP application can still expose the underlying runtime bug.&lt;/p&gt;

&lt;p&gt;The PHP project's &lt;a href="https://www.php.net/archive/2026.php" rel="noopener noreferrer"&gt;security advisory&lt;/a&gt; specifically calls out web-serving installations using &lt;strong&gt;PHP-FPM, Apache mod_php, php-cgi, FrankenPHP, and LiteSpeed&lt;/strong&gt;. It considers &lt;strong&gt;mod_php&lt;/strong&gt; especially high-risk because an exploit was demonstrated before application code executes.&lt;/p&gt;

&lt;p&gt;That last detail matters: an authentication check inside your application cannot protect code that runs before your application gets control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which PHP versions have the fix?
&lt;/h2&gt;

&lt;p&gt;The PHP project published these upstream security releases on &lt;strong&gt;October 21, 2026&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz1304xp5ynzb1n1blrzi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz1304xp5ynzb1n1blrzi.png" alt="PHP2Shell fixed upstream PHP versions: 8.5.12, 8.4.27, 8.3.36, and 8.2.35" width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PHP branch&lt;/th&gt;
&lt;th&gt;Fixed upstream version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;8.5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8.5.12&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8.4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8.4.27&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8.3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8.3.36&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8.2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8.2.35&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;PHP 8.1 and older are end-of-life upstream.&lt;/strong&gt; Some operating-system vendors and long-term-support providers offer patched, backported packages. If you're using one, check its actual security advisory: a vendor-patched package may retain an older-looking version number.&lt;/p&gt;

&lt;p&gt;For unsupported installations without a trusted backport, plan a move to a supported PHP release. PHP 8.2 itself reaches end of life at the end of &lt;strong&gt;December 2026&lt;/strong&gt;, so treating 8.2.35 as a permanent destination would be a mistake.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the PHP that's running, not just the PHP you installed
&lt;/h2&gt;

&lt;p&gt;A quick version check is useful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also has a trap: &lt;strong&gt;that command checks the CLI interpreter&lt;/strong&gt;. Your web server could be using a different PHP-FPM pool, binary, container, or managed hosting runtime.&lt;/p&gt;

&lt;p&gt;For example, in a Docker Compose deployment where the PHP service is named &lt;code&gt;php&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;php php &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And on a Linux host using PHP-FPM, you can start by locating the running workers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ps &lt;span class="nt"&gt;-eo&lt;/span&gt; pid,args | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'[p]hp-fpm'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neither command is a universal proof that every web worker is patched. Check the actual service or container image deployed behind each site, the installed package build, and the vendor's advisory. Redeploy or restart affected workers after applying the fix, then verify the running build again.&lt;/p&gt;

&lt;p&gt;For a small deployment, this may take minutes. Across several hosts, staging environments, inherited websites, and containers pinned to old image tags, the &lt;strong&gt;inventory&lt;/strong&gt; is often the harder part.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd check today
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;List internet-facing PHP services.&lt;/strong&gt; Include WordPress sites, internal admin panels exposed through VPNs or proxies, containerized applications, and forgotten staging instances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identify the web-serving PHP build.&lt;/strong&gt; Don't rely on local development versions or a single &lt;code&gt;php -v&lt;/code&gt; result.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apply the upstream fix or a confirmed vendor backport.&lt;/strong&gt; Rebuild images where necessary, restart affected workers, and verify what is actually running.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review recent errors and suspicious traffic.&lt;/strong&gt; Wiz recommends looking for unusual multipart requests, repeated memory-limit failures or HTTP 500 responses, unexpected &lt;code&gt;PHPSESSID&lt;/code&gt; response cookies, and unexplained PHP worker churn. These are investigation clues, &lt;strong&gt;not proof of compromise&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep WAF protections enabled, but patch anyway.&lt;/strong&gt; &lt;a href="https://blog.cloudflare.com/php-vulnerabilities-waf-protection" rel="noopener noreferrer"&gt;Cloudflare reported&lt;/a&gt; deploying protections and observing attack traffic on October 21. Its own guidance still tells customers to update PHP.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you can't patch immediately, follow the temporary exposure-reduction measures in the &lt;a href="https://www.php.net/archive/2026.php" rel="noopener noreferrer"&gt;official PHP advisory&lt;/a&gt;. Some configuration changes block demonstrated attack paths, but &lt;strong&gt;PHP explicitly says that none is a complete substitute for updating&lt;/strong&gt;. Avoid treating a single WAF rule or INI setting as a fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does PHP2Shell really affect 70% of the internet?
&lt;/h2&gt;

&lt;p&gt;You'll see that figure in headlines. &lt;a href="https://w3techs.com/technologies/comparison/pl-php" rel="noopener noreferrer"&gt;W3Techs reported roughly 69% PHP usage in October 2026&lt;/a&gt;, but its denominator is &lt;strong&gt;websites whose server-side programming language it can identify&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That percentage is not a count of unpatched servers. It certainly isn't a count of compromised sites. Actual exposure depends on PHP versions, runtime configurations, and applied vendor fixes.&lt;/p&gt;

&lt;p&gt;The risk is serious enough without turning a usage statistic into an incident statistic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AI is part of this story
&lt;/h2&gt;

&lt;p&gt;Researchers at &lt;a href="https://www.wiz.io/blog/php2shell-php-rce" rel="noopener noreferrer"&gt;Wiz&lt;/a&gt; describe using frontier AI models alongside debugging and testing tools to discover, develop, and validate exploit chains. The underlying memory-safety bugs had existed for years. The hard part was chaining them into reliable behavior across different environments.&lt;/p&gt;

&lt;p&gt;AI-assisted research helped reduce that effort. The same development should concern both attackers and defenders: difficult bugs may become easier to investigate, reproduce, and exploit. In this case, PHP maintainers and infrastructure providers also collaborated on patches and protections before public disclosure.&lt;/p&gt;

&lt;p&gt;Teams using PHP, .NET, Node.js, and other runtimes face the same inventory problem. A clean application dependency scan doesn't tell you whether the interpreter, base image, or web worker underneath is safe.&lt;/p&gt;

&lt;p&gt;A change to &lt;code&gt;composer.lock&lt;/code&gt; cannot close PHP2Shell if the live PHP worker still runs an old build. &lt;strong&gt;Finish with evidence from the deployed runtime&lt;/strong&gt;, not just a merged pull request.&lt;/p&gt;




&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.php.net/archive/2026.php" rel="noopener noreferrer"&gt;PHP Project — October 21 security advisory and fixed releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.wiz.io/blog/php2shell-php-rce" rel="noopener noreferrer"&gt;Wiz Research — PHP2Shell technical analysis and demonstrations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.cloudflare.com/php-vulnerabilities-waf-protection" rel="noopener noreferrer"&gt;Cloudflare — WAF protections and observed attack traffic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/NCSC-NL/php2shell" rel="noopener noreferrer"&gt;NCSC-NL — operational PHP2Shell guidance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>php</category>
      <category>security</category>
      <category>webdev</category>
      <category>cybersecurity</category>
    </item>
  </channel>
</rss>
