<?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>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>
    <item>
      <title>Ubuntu 26.10 Finishes the Rust Coreutils Switch. What Should Developers Test?</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Wed, 21 Oct 2026 13:08:28 +0000</pubDate>
      <link>https://dev.to/bargan/ubuntu-2610-finishes-the-rust-coreutils-switch-what-should-developers-test-3d4b</link>
      <guid>https://dev.to/bargan/ubuntu-2610-finishes-the-rust-coreutils-switch-what-should-developers-test-3d4b</guid>
      <description>&lt;p&gt;A deployment script can carry years of undocumented assumptions: a particular &lt;code&gt;cp&lt;/code&gt; option, an error string parsed by a build step, or a crypto library loaded through an old interface. Most of the time, those assumptions stay invisible.&lt;/p&gt;

&lt;p&gt;Ubuntu 26.10 &lt;em&gt;Stonking Stingray&lt;/em&gt;, released on October 15, 2026, gives developers a reason to look for them. Its default &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;mv&lt;/code&gt;, and &lt;code&gt;rm&lt;/code&gt; now come from &lt;a href="https://github.com/uutils/coreutils" rel="noopener noreferrer"&gt;Rust-based uutils&lt;/a&gt;. It also ships &lt;a href="https://documentation.ubuntu.com/release-notes/26.10/" rel="noopener noreferrer"&gt;OpenSSL 4&lt;/a&gt;, a refreshed language and compiler stack, ROCm 10, and a Linux 7.3-based kernel.&lt;/p&gt;

&lt;p&gt;There is a catch: 26.10 is an interim release, supported &lt;strong&gt;only through July 2027&lt;/strong&gt;. That makes the decision different for a CI image, a personal workstation, and a production server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Ubuntu 26.10 is worth testing, not blindly adopting. Check GNU-versus-uutils behavior, OpenSSL 4 dependencies, and your actual build pipeline. Containers cover userspace regressions; kernel and driver changes need a VM or host. For long-lived production systems, 26.04 LTS remains the sensible default.&lt;/p&gt;

&lt;p&gt;Here is the checklist I would use before changing a real workload. &lt;strong&gt;The commands are reproducible suggestions, not results from a benchmark I ran.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  First, understand what changed in &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;mv&lt;/code&gt;, and &lt;code&gt;rm&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Ubuntu had already moved many command-line utilities to uutils in earlier releases. The three remaining GNU holdouts — &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;mv&lt;/code&gt;, and &lt;code&gt;rm&lt;/code&gt; — stayed on GNU in 26.04 LTS because of unresolved compatibility concerns. &lt;a href="https://documentation.ubuntu.com/release-notes/26.10/" rel="noopener noreferrer"&gt;Canonical says the migration is complete in 26.10&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The language behind a command is usually irrelevant to a shell script. Its observable behavior is not. Consider the things scripts routinely depend on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;accepted flags and option combinations;&lt;/li&gt;
&lt;li&gt;exit statuses, especially under &lt;code&gt;set -e&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;the precise output of &lt;code&gt;--version&lt;/code&gt; and error messages;&lt;/li&gt;
&lt;li&gt;symbolic links, permissions, ownership, and cross-filesystem moves;&lt;/li&gt;
&lt;li&gt;behavior on unusual filenames or non-UTF-8 input.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The uutils project aims to be GNU-compatible. It does not claim every GNU edge case is already identical. An &lt;a href="https://github.com/uutils/coreutils/issues/14705" rel="noopener noreferrer"&gt;upstream compatibility tracker&lt;/a&gt; still lists options such as &lt;code&gt;cp --keep-directory-symlink&lt;/code&gt; and &lt;code&gt;mv --no-copy&lt;/code&gt; as missing in its September comparison. That is a reason to inspect the &lt;em&gt;packaged version you actually run&lt;/em&gt;, not evidence that every installation of 26.10 currently fails those cases.&lt;/p&gt;

&lt;p&gt;An independent &lt;a href="https://dev.to/devopsdaily/ubuntu-2610-moves-cp-mv-and-rm-to-rust-we-ran-162-shell-commands-through-gnu-and-uutils-to-see-42o2"&gt;DevOps Daily comparison&lt;/a&gt; tested 162 command cases in an Ubuntu 26.10 &lt;strong&gt;beta&lt;/strong&gt; environment and reported 150 byte-identical captured results and 12 differences. The differences included version identification, Unicode handling, exit codes, partial decoding, and diagnostic wording.&lt;/p&gt;

&lt;p&gt;Those are &lt;strong&gt;their measurements&lt;/strong&gt;, not mine; 162 selected cases do not measure the entire compatibility surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  A ten-minute smoke test that stays inside a temp directory
&lt;/h2&gt;

&lt;p&gt;Start with identity. This is safe to run on the system you intend to test:&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;cat&lt;/span&gt; /etc/os-release | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'^(NAME|VERSION|VERSION_ID)='&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;tool &lt;span class="k"&gt;in &lt;/span&gt;&lt;span class="nb"&gt;cp mv rm&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;'%s: '&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tool&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tool&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--version&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Ubuntu's prefixed GNU tools are available, the following probe checks option visibility without replacing system binaries. The script prints &lt;em&gt;observations&lt;/em&gt;, not predetermined pass/fail claims:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;pair &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s1"&gt;'cp --keep-directory-symlink'&lt;/span&gt; &lt;span class="s1"&gt;'mv --no-copy'&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;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; tool option &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pair&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;bin &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tool&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"gnu&lt;/span&gt;&lt;span class="nv"&gt;$tool&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$bin&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s: not installed\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$bin&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$bin&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--help&lt;/span&gt; 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-Fq&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$option&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s: %s listed\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$bin&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$option&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else
      &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s: %s NOT listed\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$bin&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$option&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi
  done
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--help&lt;/code&gt; is only an initial check. It cannot establish that an option has correct semantics. Follow it with fixtures from your actual build and deployment workflows.&lt;/p&gt;

&lt;p&gt;For a small, non-destructive filesystem comparison, run this in a &lt;strong&gt;disposable Ubuntu 26.10 VM or container&lt;/strong&gt; where &lt;code&gt;gnucp&lt;/code&gt; and &lt;code&gt;gnumv&lt;/code&gt; are available:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;for &lt;/span&gt;bin &lt;span class="k"&gt;in &lt;/span&gt;&lt;span class="nb"&gt;cp mv &lt;/span&gt;gnucp gnumv&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$bin&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Missing &lt;/span&gt;&lt;span class="nv"&gt;$bin&lt;/span&gt;&lt;span class="s2"&gt;; cannot compare implementations"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="nb"&gt;exit &lt;/span&gt;2
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nv"&gt;work&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;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;'rm -rf -- "$work"'&lt;/span&gt; EXIT

&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$work&lt;/span&gt;&lt;span class="s2"&gt;/input/sub"&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'alpha\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$work&lt;/span&gt;&lt;span class="s2"&gt;/input/a.txt"&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'beta\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$work&lt;/span&gt;&lt;span class="s2"&gt;/input/sub/b.txt"&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;implementation &lt;span class="k"&gt;in &lt;/span&gt;uutils gnu&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;
  &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$implementation&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; gnu &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'gnu'&lt;/span&gt;

  &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$work&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$implementation&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;prefix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;cp"&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$work&lt;/span&gt;&lt;span class="s2"&gt;/input"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$work&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$implementation&lt;/span&gt;&lt;span class="s2"&gt;/copied"&lt;/span&gt;

  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;prefix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;mv"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$work&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$implementation&lt;/span&gt;&lt;span class="s2"&gt;/copied/a.txt"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$work&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$implementation&lt;/span&gt;&lt;span class="s2"&gt;/moved.txt"&lt;/span&gt;

  &lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$work&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$implementation&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;sha256sum&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; + |
      &lt;span class="nv"&gt;LC_ALL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;C &lt;span class="nb"&gt;sort&lt;/span&gt;
  &lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$work&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$implementation&lt;/span&gt;&lt;span class="s2"&gt;.manifest"&lt;/span&gt;
&lt;span class="k"&gt;done

&lt;/span&gt;diff &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$work&lt;/span&gt;&lt;span class="s2"&gt;/uutils.manifest"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$work&lt;/span&gt;&lt;span class="s2"&gt;/gnu.manifest"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A zero exit code means &lt;strong&gt;this tiny fixture&lt;/strong&gt; produced matching file paths and content hashes.&lt;/p&gt;

&lt;p&gt;It says nothing about ACLs, extended attributes, sparse files, symlink semantics, timestamps, ownership, or error handling. Add those deliberately when your application requires them. You can also capture stdout, stderr, exit codes, and the resulting tree for each case.&lt;/p&gt;

&lt;p&gt;One more practical point: a &lt;code&gt;--version&lt;/code&gt; check that searches for the string &lt;code&gt;GNU&lt;/code&gt; can silently select the wrong branch in a legacy script. Search your repositories for these probes before an upgrade.&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%2Fdzm1wbqht7xsbvwumwsy.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%2Fdzm1wbqht7xsbvwumwsy.png" alt="A technical map of Ubuntu 26.10 compatibility checks across shell commands, crypto, language runtimes, kernel and GPU software." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1. A testing map compiled from Canonical's release notes; it does not represent benchmark results.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenSSL 4 may be the harder migration
&lt;/h2&gt;

&lt;p&gt;The Rust switch attracts attention because everyone recognizes &lt;code&gt;cp&lt;/code&gt;. OpenSSL 4 deserves at least as much attention if you maintain native software, hardware-security-module integrations, or older TLS infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.openssl.org/4.0/man7/ossl-removed-api/" rel="noopener noreferrer"&gt;OpenSSL 4 removed the ENGINE API&lt;/a&gt;. Ubuntu's &lt;a href="https://documentation.ubuntu.com/release-notes/26.10/" rel="noopener noreferrer"&gt;26.10 notes&lt;/a&gt; specifically explain that PKCS#11-engine configurations must move to the &lt;strong&gt;PKCS#11 provider&lt;/strong&gt; model.&lt;/p&gt;

&lt;p&gt;OpenSSL 4 also removes older interfaces and legacy protocol behavior. These changes are relevant even if your own application never calls the &lt;code&gt;openssl&lt;/code&gt; executable directly.&lt;/p&gt;

&lt;p&gt;Useful read-only inspection commands are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl version &lt;span class="nt"&gt;-a&lt;/span&gt;
openssl list &lt;span class="nt"&gt;-providers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then inspect the OpenSSL configuration used by your &lt;em&gt;application&lt;/em&gt; for references to &lt;code&gt;engine_id&lt;/code&gt;, &lt;code&gt;dynamic_path&lt;/code&gt;, and &lt;code&gt;pkcs11&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Redact secrets before sharing logs. Rebuild native dependencies against the target headers, and test a real TLS handshake or HSM operation. &lt;code&gt;openssl version&lt;/code&gt; alone cannot prove application compatibility.&lt;/p&gt;

&lt;p&gt;For older systems, this is a more specific question than “Does it support OpenSSL 4?”:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is a provider available for the key material and algorithms the application actually uses?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Newer toolchains: choose tests that match the dependency
&lt;/h2&gt;

&lt;p&gt;Canonical lists the following in its &lt;a href="https://documentation.ubuntu.com/release-notes/26.10/" rel="noopener noreferrer"&gt;Ubuntu 26.10 release notes&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;Component&lt;/th&gt;
&lt;th&gt;Listed version&lt;/th&gt;
&lt;th&gt;A useful regression check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;3.14.7&lt;/td&gt;
&lt;td&gt;Build a fresh environment, install native wheels, and run the suite&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;1.27&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;go test ./...&lt;/code&gt;, &lt;code&gt;go vet ./...&lt;/code&gt;, code generation, cross-builds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLVM&lt;/td&gt;
&lt;td&gt;22.1.8&lt;/td&gt;
&lt;td&gt;Rebuild LLVM plugins, generated IR, sanitizers, native extensions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.NET&lt;/td&gt;
&lt;td&gt;10.0.112&lt;/td&gt;
&lt;td&gt;Check &lt;code&gt;dotnet --info&lt;/code&gt;, restore, build, test, publish, native dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ROCm&lt;/td&gt;
&lt;td&gt;10.0&lt;/td&gt;
&lt;td&gt;Check supported GPUs, rebuild AMD SMI users, run actual GPU workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kernel&lt;/td&gt;
&lt;td&gt;7.3-based&lt;/td&gt;
&lt;td&gt;Test drivers, DKMS, eBPF, virtualization and filesystem behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The version numbers are those &lt;strong&gt;listed in the release notes&lt;/strong&gt;, not a claim about the latest patch currently installed on every updated machine.&lt;/p&gt;

&lt;p&gt;ROCm has a particularly concrete breaking boundary: Canonical documents an AMD SMI SONAME change to &lt;code&gt;libamd_smi.so.27&lt;/code&gt;, removed deprecated AMD SMI APIs, and removal of &lt;code&gt;rocsparse_indextype_u16&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Software that links to the changed library needs rebuilding. For GPU work, verify the exact device against the &lt;a href="https://documentation.ubuntu.com/release-notes/26.10/" rel="noopener noreferrer"&gt;supported hardware list&lt;/a&gt; before planning a migration.&lt;/p&gt;

&lt;p&gt;The Ubuntu release image also uses a &lt;code&gt;7.3.0-9.9&lt;/code&gt; kernel built from upstream &lt;strong&gt;7.3-rc6&lt;/strong&gt;. The upstream 7.3 final release arrived after Ubuntu 26.10's release date.&lt;/p&gt;

&lt;p&gt;To troubleshoot a driver issue, record &lt;code&gt;uname -r&lt;/code&gt; and the installed Ubuntu package rather than just writing “Linux 7.3”.&lt;/p&gt;

&lt;h3&gt;
  
  
  A container is not a kernel test
&lt;/h3&gt;

&lt;p&gt;You can use a 26.10 container to detect userspace regressions, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; ubuntu:26.10 bash &lt;span class="nt"&gt;-lc&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'cat /etc/os-release | grep VERSION=; cp --version | head -1'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify image availability and pin a digest for repeatable CI.&lt;/p&gt;

&lt;p&gt;But the container shares its host kernel. &lt;code&gt;uname -r&lt;/code&gt; inside that container reports the &lt;strong&gt;host&lt;/strong&gt; kernel, not a separately installed Ubuntu 26.10 kernel.&lt;/p&gt;

&lt;p&gt;Kernel modules, DKMS, real device drivers, and most GPU integration problems need a VM or physical test host.&lt;/p&gt;

&lt;p&gt;That distinction matters when someone says an upgrade passed “in Docker.” It may have passed only the userspace portion.&lt;/p&gt;

&lt;h2&gt;
  
  
  There are desktop and operational changes too
&lt;/h2&gt;

&lt;p&gt;Ubuntu 26.10 uses &lt;strong&gt;GNOME 51&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It also offers &lt;strong&gt;Myna&lt;/strong&gt;, an &lt;em&gt;optional, not-preinstalled&lt;/em&gt; local speech-to-text technology preview: transcription runs on the device rather than sending audio to a network service.&lt;/p&gt;

&lt;p&gt;These may matter to workstation users, but neither answers whether a production build is ready to move. &lt;a href="https://documentation.ubuntu.com/release-notes/26.10/" rel="noopener noreferrer"&gt;Canonical documents both&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On servers, inspect SSH authentication before touching a remote box.&lt;/p&gt;

&lt;p&gt;In 26.10, GSSAPI/Kerberos-capable OpenSSH packages are split into the &lt;code&gt;openssh-gssapi&lt;/code&gt; family; the ordinary client/server packages do not provide that feature. If access depends on it, stage and test that exact access path first.&lt;/p&gt;

&lt;p&gt;Canonical also says upgrades from 26.04 LTS with &lt;strong&gt;TPM-backed full-disk encryption&lt;/strong&gt; were not supported at the initial 26.10 release.&lt;/p&gt;

&lt;p&gt;Consult the current known-issues section before any encrypted-system upgrade, because restrictions may be updated during the release lifecycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  My upgrade decision would depend on the workload
&lt;/h2&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%2F5gwp6e1jmaw3ap7pxgza.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%2F5gwp6e1jmaw3ap7pxgza.png" alt="A decision guide showing Ubuntu 26.04 LTS as a production baseline and Ubuntu 26.10 as a parallel test lane before a staged rollout." width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2. A recommended rollout sequence, not an Ubuntu policy or a guarantee of compatibility.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For a &lt;strong&gt;developer laptop&lt;/strong&gt;, 26.10 is attractive if your hardware and dependencies need the new stack and you are comfortable with a nine-month support window. Save a recovery path first.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;CI&lt;/strong&gt;, add 26.10 &lt;em&gt;in parallel&lt;/em&gt; with the existing image.&lt;/p&gt;

&lt;p&gt;Compare builds, packages, automated tests, and shell scripts. Use a container job for userspace; use a VM or host runner when kernel behavior is part of the test.&lt;/p&gt;

&lt;p&gt;Do not assume &lt;code&gt;ubuntu-26.10&lt;/code&gt; is a valid GitHub-hosted Actions runner label just because an Ubuntu version exists.&lt;/p&gt;

&lt;p&gt;For a &lt;strong&gt;production server&lt;/strong&gt;, my default would remain 26.04 LTS unless there is a specific capability worth the maintenance and migration cost.&lt;/p&gt;

&lt;p&gt;Check crypto configuration, SSH/Kerberos, drivers, and a tested rollback path before rollout. An interim release is a poor substitute for a support-lifecycle decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  The checklist I'd save before upgrading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shell compatibility:&lt;/strong&gt; Inventory the exact command implementations and GNU-only options your scripts use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavioral testing:&lt;/strong&gt; Run real shell fixtures under GNU and uutils. Compare side effects, not just output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automation:&lt;/strong&gt; Find scripts that parse version strings, diagnostic messages, or exit codes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenSSL 4:&lt;/strong&gt; Rebuild native integrations and migrate ENGINE-based PKCS#11 configurations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Toolchains:&lt;/strong&gt; Install Python dependencies from scratch. Build and test .NET, Go, and LLVM projects where applicable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GPU workloads:&lt;/strong&gt; Validate ROCm against supported hardware and rebuild affected AMD SMI consumers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kernel compatibility:&lt;/strong&gt; Test drivers, DKMS, and other kernel-dependent features outside containers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System access:&lt;/strong&gt; Verify Kerberos/GSSAPI authentication and TPM-backed encryption upgrade constraints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rollback readiness:&lt;/strong&gt; Confirm the nine-month support window and rehearse a recovery procedure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My practical takeaway is to &lt;strong&gt;add Ubuntu 26.10 to the compatibility matrix before making it a default&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Even if you plan to stay on LTS, this release is a useful early warning for assumptions buried in tooling and deployment scripts.&lt;/p&gt;

&lt;p&gt;Have you seen a real GNU-versus-uutils incompatibility in a build or deployment?&lt;/p&gt;

&lt;p&gt;A small reproducible case — exact command, package version, expected and observed behavior — would make a far more useful comment than a generic upgrade verdict.&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ubuntu.com/blog/canonical-releases-ubuntu-26-10-stonking-stingray" rel="noopener noreferrer"&gt;Canonical — Ubuntu 26.10 release announcement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://documentation.ubuntu.com/release-notes/26.10/" rel="noopener noreferrer"&gt;Ubuntu 26.10 release notes (features, breaking changes, known issues)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/uutils/coreutils" rel="noopener noreferrer"&gt;uutils coreutils project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/uutils/coreutils/issues/14705" rel="noopener noreferrer"&gt;uutils GNU-compatibility issue tracker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/devopsdaily/ubuntu-2610-moves-cp-mv-and-rm-to-rust-we-ran-162-shell-commands-through-gnu-and-uutils-to-see-42o2"&gt;DevOps Daily's independent 162-case test on 26.10 beta&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.openssl.org/4.0/man7/ossl-removed-api/" rel="noopener noreferrer"&gt;OpenSSL 4.0 removed APIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.openssl.org/master/man7/ossl-guide-migration/" rel="noopener noreferrer"&gt;OpenSSL migration guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Technical scope: release documentation and community test evidence reviewed October 21, 2026. The shell examples are provided for readers to execute and verify on their own systems; no first-party Ubuntu 26.10 benchmark is claimed in this article.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ubuntu</category>
      <category>linux</category>
      <category>devops</category>
      <category>rust</category>
    </item>
    <item>
      <title>What did that Claude Code session actually cost? Spend, quota and history in one place</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Wed, 21 Oct 2026 06:24:40 +0000</pubDate>
      <link>https://dev.to/bargan/what-did-that-claude-code-session-actually-cost-spend-quota-and-history-in-one-place-2b6l</link>
      <guid>https://dev.to/bargan/what-did-that-claude-code-session-actually-cost-spend-quota-and-history-in-one-place-2b6l</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 is at &lt;a href="https://ccdeck.dev/guides/cost-and-quota/" rel="noopener noreferrer"&gt;ccdeck.dev/guides/cost-and-quota&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Three questions I kept asking
&lt;/h2&gt;

&lt;p&gt;With several agent sessions running, I want to know where the tokens went and whether I have enough quota to finish. Those are different questions, so I keep three numbers in view:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;How much quota is left&lt;/strong&gt; before the 5-hour window cuts me off — and will I hit it before it resets?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What did this one session cost?&lt;/strong&gt; The big refactor, or the one that went in circles for an hour.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where did this month go?&lt;/strong&gt; Which days, which models, which projects.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ccdeck answers all three on one page, and is careful to say which number comes from where. This post walks through them.&lt;br&gt;
&lt;/p&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;h2&gt;
  
  
  The topbar: this month
&lt;/h2&gt;

&lt;p&gt;The topbar always shows &lt;strong&gt;this month&lt;/strong&gt;: what you have spent since the 1st, in tokens and dollars, across every Claude Code and Codex session on the machine. For the rest, press &lt;strong&gt;&lt;code&gt;U&lt;/code&gt;&lt;/strong&gt; (or click &lt;strong&gt;Usage&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/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy760ypbgkuxztd5wlfkc.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%2Fy760ypbgkuxztd5wlfkc.webp" alt="The Usage panel: Claude quota with 5-hour and 7-day bars, Codex quota with its plan, then today's spend by model and by session" width="560" height="1776"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data. Both CLIs are watched, so both quotas are drawn.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Question 1 — how much quota is left?
&lt;/h2&gt;

&lt;p&gt;Each bar is one quota window:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude quota&lt;/strong&gt; has a 5-hour window and a 7-day window, plus Sonnet (7d) and Opus (7d) when Anthropic reports them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codex quota&lt;/strong&gt; has a bar for each window OpenAI reports, with your plan beside the heading.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Under each bar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;resets in&lt;/strong&gt; is the time until the window starts again;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;on pace&lt;/strong&gt; or &lt;strong&gt;4% over pace&lt;/strong&gt; compares your use with the time gone;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;runs out in&lt;/strong&gt; means that, at the rate so far, you would reach 100% before the reset;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;used up&lt;/strong&gt; means the window is full.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A bar turns amber from 70% and red from 90%. The age beside a heading (such as &lt;code&gt;2m ago&lt;/code&gt;) says how old the reading is, and an incident on Claude Code, the Claude API or Codex shows as a line under that quota's heading, linking to the status page.&lt;/p&gt;

&lt;p&gt;You need a subscription for this part: Claude Code signed in with a Claude plan, or Codex signed in with &lt;code&gt;codex login&lt;/code&gt; and a ChatGPT account. With an API key, or Bedrock or Vertex, there is no quota window — the panel says &lt;strong&gt;No quota to show&lt;/strong&gt;, and your spend still shows.&lt;/p&gt;

&lt;p&gt;If you juggle more than one Claude account, the accounts panel shows each account's 5-hour and 7-day usage side by side; the &lt;a href="https://ccdeck.dev/guides/claude-accounts/" rel="noopener noreferrer"&gt;accounts guide&lt;/a&gt; covers it, and I wrote it up separately in a post on sharing accounts between machines.&lt;/p&gt;




&lt;h2&gt;
  
  
  Question 2 — what did this session cost?
&lt;/h2&gt;

&lt;p&gt;Every session's &lt;strong&gt;card&lt;/strong&gt;, its row in the session list and its detail panel show its spend, including its subagents', priced from ccdeck's own rate table.&lt;/p&gt;

&lt;p&gt;In the Usage panel, &lt;strong&gt;By session&lt;/strong&gt; lists a row per session active in the period. A session that has left the canvas shows the end of its id, like &lt;code&gt;8c3f0e17&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two markers are worth knowing, because they are the deck refusing to guess:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;not priced&lt;/strong&gt; stands where a dollar figure would be when there is no published rate for the model. Its tokens are counted; its dollars are not. New rates arrive with new releases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A figure ending in &lt;code&gt;+&lt;/code&gt;&lt;/strong&gt;, such as &lt;strong&gt;$11.19+&lt;/strong&gt;, is a minimum: some of its tokens are on a model with no rate. Hover it — a card names the model, and the Usage panel and the Projects report say how many tokens have no rate.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Question 3 — where did the month go?
&lt;/h2&gt;

&lt;p&gt;Under the quotas, pick &lt;strong&gt;today&lt;/strong&gt;, &lt;strong&gt;month&lt;/strong&gt; or &lt;strong&gt;all&lt;/strong&gt;. Month starts on the 1st, not 30 days ago; all is every transcript on disk.&lt;/p&gt;

&lt;p&gt;These totals come from &lt;a href="https://github.com/ccusage/ccusage" rel="noopener noreferrer"&gt;ccusage&lt;/a&gt;, a separate tool that reads the transcripts both CLIs leave on disk. The deck installs its own copy with npm the first time it needs one, or uses a &lt;code&gt;ccusage&lt;/code&gt; already on your PATH. Because ccusage reads the transcripts, the totals cover every session on the machine, &lt;strong&gt;even ones no longer on the canvas&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usage history&lt;/strong&gt; (press &lt;strong&gt;&lt;code&gt;H&lt;/code&gt;&lt;/strong&gt;) opens on 30 days, with 7, 14 and 90 a click away. Click a day's bar for its tokens and its cost by CLI and by model.&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%2F5edijmsu5o9flc5u20oa.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%2F5edijmsu5o9flc5u20oa.webp" alt="Usage history over 30 days: total cost split between Claude Code and Codex, daily bars stacked by model, and the selected day's breakdown" width="799" height="587"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data. The last bar is today, clicked.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  By project, per account
&lt;/h3&gt;

&lt;p&gt;For Claude Code, the accounts panel (&lt;code&gt;A&lt;/code&gt;) has a &lt;strong&gt;Projects&lt;/strong&gt; report on each account's &lt;code&gt;⋯&lt;/code&gt; menu: what that account spent in each project, by day.&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%2Fzett1qxdbcffput4n87q.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%2Fzett1qxdbcffput4n87q.webp" alt="The Projects report for one account over 7 days: spend per project with a daily chart" width="752" height="788"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data. The footer says the dollars have been matched to ccusage's.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Work is counted for the account that was active when each message was sent. Work no account can be tied to is listed apart as &lt;strong&gt;Unattributed usage&lt;/strong&gt; and left out of the totals.&lt;/p&gt;




&lt;h2&gt;
  
  
  Read the dollars correctly
&lt;/h2&gt;

&lt;p&gt;This is the most important section of the post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;These are API prices.&lt;/strong&gt; Every dollar is the tokens priced at the providers' published per-token API rates. &lt;strong&gt;The deck does not know what your plan bills you.&lt;/strong&gt; On a subscription, think of it as "what this would have cost on the API".&lt;/li&gt;
&lt;li&gt;ccdeck's table is the &lt;strong&gt;base rate&lt;/strong&gt;: no Batch discount, no data-residency multiplier. Claude Code's fast mode is priced at its own rates. Codex session cards use the standard rate without a long-context surcharge; the Session usage view can also compare Standard, Fast and Flex estimates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ccusage measures a session and a day separately&lt;/strong&gt;, so the By session rows can add up to more than the total above them.&lt;/li&gt;
&lt;li&gt;ccusage dates sessions in &lt;strong&gt;UTC&lt;/strong&gt;, while the totals use your local day. Early in your morning, By session can say &lt;em&gt;No session is dated today yet&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It does not read your bill.&lt;/strong&gt; No invoice, no plan price, nothing from a billing API — only the transcripts on this machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Only this machine.&lt;/strong&gt; Sessions you ran elsewhere are in that machine's deck, not this one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Projects report is Claude Code only&lt;/strong&gt;; Codex work is never in it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quota needs a subscription login.&lt;/strong&gt; API-key, Bedrock and Vertex users get spend but no quota bars.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Totals need ccusage.&lt;/strong&gt; If it cannot be read, Usage can fall back to &lt;em&gt;spend on this board&lt;/em&gt;, counting only sessions on the canvas; Usage history says why. Installing npm, or putting &lt;code&gt;ccusage&lt;/code&gt; on the deck's PATH, fixes it.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try it
&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;Run a session, then press &lt;code&gt;U&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Guide: &lt;a href="https://ccdeck.dev/guides/cost-and-quota/" rel="noopener noreferrer"&gt;https://ccdeck.dev/guides/cost-and-quota/&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;
&lt;/li&gt;
&lt;li&gt;Licence: AGPL-3.0&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>claude</category>
    </item>
    <item>
      <title>Claude Code Added GZIP in 2026. Why Not Brotli?</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Mon, 19 Oct 2026 05:32:26 +0000</pubDate>
      <link>https://dev.to/bargan/claude-code-added-gzip-in-2026-why-not-brotli-p7</link>
      <guid>https://dev.to/bargan/claude-code-added-gzip-in-2026-why-not-brotli-p7</guid>
      <description>&lt;p&gt;A developer captured a Claude Code API request that was &lt;strong&gt;594,750 bytes&lt;/strong&gt;. After GZIP compression, the body was &lt;strong&gt;289,145 bytes&lt;/strong&gt;. That's about 51% less data to upload, without removing a word from the request.&lt;/p&gt;

&lt;p&gt;Those numbers come from a &lt;a href="https://github.com/anthropics/claude-code/issues/90457" rel="noopener noreferrer"&gt;community investigation&lt;/a&gt;, not an official Anthropic benchmark. But they help explain a small change that shipped on &lt;strong&gt;October 14, 2026&lt;/strong&gt;: &lt;a href="https://github.com/anthropics/claude-code/releases/tag/v2.1.300" rel="noopener noreferrer"&gt;Claude Code v2.1.300&lt;/a&gt; began compressing large requests to &lt;code&gt;api.anthropic.com&lt;/code&gt; with GZIP, including when telemetry is disabled.&lt;/p&gt;

&lt;p&gt;GZIP has been around since 1992. Brotli is newer and often produces smaller compressed files. So why pick GZIP for an AI coding tool in 2026? And what should we use in our own APIs?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why an AI coding assistant uploads so much text
&lt;/h2&gt;

&lt;p&gt;A coding assistant's request can include conversation history, tool definitions, tool results, and other context. What gets included varies, but many requests contain a lot of repeated, structured text.&lt;/p&gt;

&lt;p&gt;In August 2026, the author of &lt;a href="https://github.com/anthropics/claude-code/issues/90457" rel="noopener noreferrer"&gt;Claude Code issue #90457&lt;/a&gt; inspected requests from version 2.1.246 using a local proxy. They reported that the bodies were being sent without compression. Their measurements included:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Request body&lt;/th&gt;
&lt;th&gt;Bytes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Before compression&lt;/td&gt;
&lt;td&gt;594,750&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After GZIP&lt;/td&gt;
&lt;td&gt;289,145&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Saved&lt;/td&gt;
&lt;td&gt;305,605 (about 51%)&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%2Fs09blr8xwzgcoees0lf1.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%2Fs09blr8xwzgcoees0lf1.png" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The same report says their proxy avoided 185 MB of uploads over roughly three hours of use on one machine. Those results are specific to that person's workload and setup.&lt;/p&gt;

&lt;p&gt;To put the single-request saving in perspective: transferring 305,605 fewer bytes on a 2 Mbps upload link saves approximately &lt;strong&gt;1.2 seconds of raw transmission time&lt;/strong&gt;. That's a calculation, not a measured improvement in Claude's response time. CPU work, protocol overhead, network conditions, and model inference all affect actual latency.&lt;/p&gt;

&lt;p&gt;The official v2.1.300 notes confirm that large API requests now use GZIP. They also document an escape hatch for troubleshooting:&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_GZIP_REQUEST_BODIES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What GZIP actually does
&lt;/h2&gt;

&lt;p&gt;GZIP is a lossless compression format built around the DEFLATE algorithm. DEFLATE combines two ideas: refer back to repeated byte sequences instead of writing them again, and use shorter codes for frequently occurring symbols.&lt;/p&gt;

&lt;p&gt;Think of a JSON document containing the key &lt;code&gt;"category"&lt;/code&gt; a thousand times. A compressor can represent much of that repetition more efficiently. The receiver decompresses the bytes and gets the original JSON back.&lt;/p&gt;

&lt;p&gt;Brotli is also lossless. It uses a different encoding design and a built-in dictionary that can help with common text patterns. It often compresses HTML, CSS, JavaScript, and JSON better, especially at stronger compression settings. The trade-off can be more CPU time spent compressing.&lt;/p&gt;

&lt;p&gt;Neither format makes your server execute application code faster. They reduce bytes transferred over the network, and compression itself costs CPU time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Request compression and response compression are different
&lt;/h2&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%2Fwln7nk07b4hlsbnbmxac.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%2Fwln7nk07b4hlsbnbmxac.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most web developers encounter compression when a browser downloads a response.&lt;/p&gt;

&lt;p&gt;The browser sends:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/api/products&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Accept-Encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;br, gzip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means it can accept Brotli (&lt;code&gt;br&lt;/code&gt;) or GZIP (&lt;code&gt;gzip&lt;/code&gt;) responses. The server might reply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;
&lt;span class="na"&gt;Content-Encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;br&lt;/span&gt;
&lt;span class="na"&gt;Vary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Accept-Encoding&lt;/span&gt;

&lt;span class="err"&gt;&amp;lt;compressed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;bytes&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Vary: Accept-Encoding&lt;/code&gt; tells HTTP caches that clients accepting different encodings may need different response representations.&lt;/p&gt;

&lt;p&gt;Claude Code's update is about the &lt;strong&gt;upload direction&lt;/strong&gt;. The client compresses a request body before sending it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;POST&lt;/span&gt; &lt;span class="nn"&gt;/v1/messages&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;
&lt;span class="na"&gt;Content-Encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gzip&lt;/span&gt;

&lt;span class="err"&gt;&amp;lt;gzip-compressed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;JSON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;bytes&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the server must support decoding GZIP request bodies. &lt;code&gt;Accept-Encoding&lt;/code&gt; describes what you want to &lt;em&gt;receive&lt;/em&gt;; &lt;code&gt;Content-Encoding&lt;/code&gt; describes how the body you're &lt;em&gt;sending&lt;/em&gt; is encoded.&lt;/p&gt;

&lt;p&gt;One more distinction matters for AI tools: compressing the HTTP request does &lt;strong&gt;not&lt;/strong&gt; reduce the number of input tokens the model processes. The server restores the original body before interpreting it. Transport compression can save bandwidth without changing context-window usage or model billing.&lt;/p&gt;

&lt;h2&gt;
  
  
  So why didn't Claude Code choose Brotli?
&lt;/h2&gt;

&lt;p&gt;In the August GitHub report, the author tested request encodings against Anthropic's API. GZIP worked. Brotli, Zstandard, and DEFLATE were rejected on the endpoint they tested. That describes the author's tests at that time, not a permanent compatibility guarantee for all Anthropic endpoints.&lt;/p&gt;

&lt;p&gt;Given those results, GZIP was the compatible option. Saving a few more bytes with Brotli wouldn't help if the receiving API couldn't decode them.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Consideration&lt;/th&gt;
&lt;th&gt;GZIP&lt;/th&gt;
&lt;th&gt;Brotli&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Typical text compression&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Often produces smaller output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compression CPU cost&lt;/td&gt;
&lt;td&gt;Generally modest at normal settings&lt;/td&gt;
&lt;td&gt;Can cost more at stronger settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Modern browser response support&lt;/td&gt;
&lt;td&gt;Broad&lt;/td&gt;
&lt;td&gt;Broad&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Precompressed static assets&lt;/td&gt;
&lt;td&gt;Useful&lt;/td&gt;
&lt;td&gt;Often a strong choice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compressed request uploads&lt;/td&gt;
&lt;td&gt;Server must support it&lt;/td&gt;
&lt;td&gt;Server must support it&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%2Fhzxk4wzhhff1xjirsrqk.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%2Fhzxk4wzhhff1xjirsrqk.png" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For static website assets, you can compress once during a build and serve the same file many times. Spending extra CPU to achieve a smaller Brotli file can make sense there.&lt;/p&gt;

&lt;p&gt;For dynamic API responses, measure both transfer size and compression time. Higher compression levels aren't automatically faster for users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it in ASP.NET Core
&lt;/h2&gt;

&lt;p&gt;We can test both response compression and request decompression with built-in ASP.NET Core middleware. This example uses plain HTTP on localhost so we don't accidentally suggest enabling HTTPS compression without considering its security implications.&lt;/p&gt;

&lt;p&gt;Create a minimal app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new web &lt;span class="nt"&gt;-n&lt;/span&gt; CompressionDemo
&lt;span class="nb"&gt;cd &lt;/span&gt;CompressionDemo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;Program.cs&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.IO.Compression&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text.Json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.ResponseCompression&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddResponseCompression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BrotliCompressionProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GzipCompressionProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BrotliCompressionProviderOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Level&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CompressionLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fastest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRequestDecompression&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseResponseCompression&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRequestDecompression&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/items"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;Enumerable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Category&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"accessory"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Available&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/echo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JsonElement&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run &lt;span class="nt"&gt;--urls&lt;/span&gt; http://localhost:5073
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Compare download sizes
&lt;/h3&gt;

&lt;p&gt;On macOS or Linux, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;encoding &lt;span class="k"&gt;in &lt;/span&gt;identity &lt;span class="nb"&gt;gzip &lt;/span&gt;br&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Accept-Encoding: &lt;/span&gt;&lt;span class="nv"&gt;$encoding&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'%{size_download}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    http://localhost:5073/items&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%-8s %s bytes\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$encoding&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$bytes&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;This counts the downloaded response-body bytes. It does not benchmark end-to-end latency, and Brotli at &lt;code&gt;CompressionLevel.Fastest&lt;/code&gt; is not guaranteed to beat GZIP for every payload.&lt;/p&gt;

&lt;p&gt;Inspect the response headers separately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; - &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Accept-Encoding: br'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  http://localhost:5073/items
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;Content-Encoding: br&lt;/code&gt; and &lt;code&gt;Vary: Accept-Encoding&lt;/code&gt; when the middleware compresses the response.&lt;/p&gt;

&lt;h3&gt;
  
  
  Send a GZIP-compressed upload
&lt;/h3&gt;

&lt;p&gt;Now send compressed JSON to &lt;code&gt;/echo&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'{"message":"hello from gzip"}'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; body.json
&lt;span class="nb"&gt;gzip&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; body.json &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; body.json.gz

curl &lt;span class="nt"&gt;-i&lt;/span&gt; http://localhost:5073/echo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Encoding: gzip'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-binary&lt;/span&gt; @body.json.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ASP.NET Core decompresses the body before the endpoint binds it as JSON. The endpoint returns the original content.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AddResponseCompression()&lt;/code&gt; and &lt;code&gt;AddRequestDecompression()&lt;/code&gt; solve different problems. Enabling the first does not automatically enable the second.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before turning this on in production
&lt;/h2&gt;

&lt;p&gt;I'd start by measuring actual requests and responses, rather than choosing an algorithm from a comparison table.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Measure realistic payloads.&lt;/strong&gt; Compare bytes, compression CPU time, and p95 latency under load. A tiny JSON response won't behave like a 500 KB response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the hosting layer.&lt;/strong&gt; IIS, a reverse proxy, or a CDN might already compress responses. Inspect the bytes reaching the client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't recompress everything.&lt;/strong&gt; JPEGs, videos, and ZIP files usually have little room left for savings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review HTTPS compression security.&lt;/strong&gt; ASP.NET Core response compression middleware disables HTTPS compression by default. Compressing secrets alongside attacker-controlled content can expose side channels such as BREACH.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limit decompressed request sizes.&lt;/strong&gt; A very small compressed upload can expand into a huge body. Keep request size limits in place to reduce decompression-bomb risk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test caching and streaming.&lt;/strong&gt; Check &lt;code&gt;Vary: Accept-Encoding&lt;/code&gt;, streamed responses, and any proxy that transforms bodies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a web API, Brotli with GZIP fallback is a reasonable response-compression setup to test. For compressed uploads, check which formats the receiving service accepts before deciding.&lt;/p&gt;

&lt;p&gt;Claude Code's change started with a developer measuring real traffic and demonstrating that a server already accepted compressed requests. If your application uploads large JSON bodies repeatedly, inspecting &lt;code&gt;Content-Encoding&lt;/code&gt; and the actual byte counts is a worthwhile first step.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/anthropics/claude-code/releases/tag/v2.1.300" rel="noopener noreferrer"&gt;Claude Code v2.1.300 release notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/anthropics/claude-code/issues/90457" rel="noopener noreferrer"&gt;Claude Code issue #90457: Compress API request bodies with gzip by default&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/aspnet/core/performance/response-compression?view=aspnetcore-10.0" rel="noopener noreferrer"&gt;Microsoft: Response compression in ASP.NET Core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/request-decompression?view=aspnetcore-10.0" rel="noopener noreferrer"&gt;Microsoft: Request decompression in ASP.NET Core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Compression" rel="noopener noreferrer"&gt;MDN: Compression in HTTP&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>performance</category>
      <category>dotnet</category>
      <category>webdev</category>
      <category>ai</category>
    </item>
    <item>
      <title>Stop running `claude login` on every machine — share your Claude Code accounts between your devices</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Mon, 19 Oct 2026 04:53:29 +0000</pubDate>
      <link>https://dev.to/bargan/stop-running-claude-login-on-every-machine-share-your-claude-code-accounts-between-your-devices-160g</link>
      <guid>https://dev.to/bargan/stop-running-claude-login-on-every-machine-share-your-claude-code-accounts-between-your-devices-160g</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 open-source tool this post uses. The step-by-step guides behind it are on &lt;a href="https://ccdeck.dev/guides/" rel="noopener noreferrer"&gt;ccdeck.dev&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I work on two machines, sometimes three. A laptop I carry around, a desktop at home, and a Windows box I test on. Claude Code runs on all of them, and on most days I switch between a work account and a personal one.&lt;/p&gt;

&lt;p&gt;That setup has one boring, recurring problem: &lt;strong&gt;a Claude login expires on the machine that has not used it for a while, and stays alive on the one that has.&lt;/strong&gt; With two or three machines that is a &lt;code&gt;claude login&lt;/code&gt; about once a week, and it always happens on the machine I am not sitting at. I sit down, start an agent, and the first thing it does is ask me to sign in again.&lt;/p&gt;

&lt;p&gt;This post shows how I fixed it with ccdeck, in three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keep several Claude accounts on one machine, and see how much quota each has left.&lt;/li&gt;
&lt;li&gt;Move those accounts to another machine &lt;strong&gt;once&lt;/strong&gt;, with a single paste.&lt;/li&gt;
&lt;li&gt;Pair the machines so that &lt;strong&gt;an expired login is copied back from a machine that still has it&lt;/strong&gt;, without me doing anything.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One thing to be clear about first: this is for &lt;strong&gt;your own accounts on your own machines&lt;/strong&gt;. It is not a way to share a subscription with other people.&lt;/p&gt;




&lt;h2&gt;
  
  
  What ccdeck is, in one paragraph
&lt;/h2&gt;

&lt;p&gt;ccdeck is a local dashboard for Claude Code and Codex CLI. You run &lt;code&gt;npx ccdeck&lt;/code&gt; (or install the desktop app), and it opens on &lt;code&gt;http://127.0.0.1:4317&lt;/code&gt; with every session on the machine drawn live: which session is waiting on you, every subagent and tool call, cost and quota. The part this post is about is its &lt;strong&gt;Claude accounts&lt;/strong&gt; panel, which sits on top of &lt;a href="https://pypi.org/project/claude-swap/" rel="noopener noreferrer"&gt;claude-swap&lt;/a&gt;, a small tool that stores more than one Claude login and switches between them. ccdeck installs claude-swap for you the first time it finds Claude Code.&lt;br&gt;
&lt;/p&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;If you want the full install walkthrough first, it is in the &lt;a href="https://ccdeck.dev/guides/first-run/" rel="noopener noreferrer"&gt;first-run guide&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1 — Several Claude accounts on one machine
&lt;/h2&gt;

&lt;p&gt;Press &lt;code&gt;A&lt;/code&gt; (or click the &lt;strong&gt;Claude accounts&lt;/strong&gt; icon at the top of the stripe on the left) to open the Claude accounts panel. Click &lt;code&gt;+&lt;/code&gt;, then &lt;strong&gt;Open the sign-in page&lt;/strong&gt;, and approve the sign-in in the browser tab that opens, as the account you want to add.&lt;/p&gt;

&lt;p&gt;The account you were using &lt;strong&gt;stays active&lt;/strong&gt;. Signing in normally replaces the live credentials, so the deck switches the previous one back as soon as the new one is recorded.&lt;/p&gt;

&lt;p&gt;Each account then gets a row with its &lt;strong&gt;5-hour&lt;/strong&gt; and &lt;strong&gt;7-day&lt;/strong&gt; usage:&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%2Fbp8eg9x79y2d7olxmnf0.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%2Fbp8eg9x79y2d7olxmnf0.webp" alt="The Other accounts list: two accounts with their 5-hour and 7-day usage, a Switch button on each, and Auto-switch underneath" width="328" height="390"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data. Figures turn amber at 70% and red at 90%.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A few things I use every day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Switch&lt;/strong&gt; on a row makes that account active. New sessions start on it; sessions already running pick it up on their next message.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;gauge&lt;/strong&gt; in the panel header opens &lt;em&gt;Account capacity&lt;/em&gt;: how many accounts are ready, and what the 5-hour and 7-day windows have left across all of them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-switch&lt;/strong&gt; (off until you turn it on) runs claude-swap's own engine every 60 seconds and moves you to another account when the active one passes a threshold you pick — 70, 80, 85, 90 or 95%, or a custom whole percentage from 50 to 99. Every account keeps its own limits; if no eligible account has room, nothing switches.&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%2Fie4sl4xd95u3f96jhm1z.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%2Fie4sl4xd95u3f96jhm1z.webp" alt="Account capacity: 3 of 3 accounts ready, with the remaining 5-hour and 7-day share across all of them" width="800" height="404"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;On a Mac:&lt;/strong&gt; the login lives in the Keychain. Start ccdeck from a Terminal window or at login, not over SSH, or claude-swap cannot read it and every row says "Keychain unreadable".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Full guide: &lt;a href="https://ccdeck.dev/guides/claude-accounts/" rel="noopener noreferrer"&gt;Switch between multiple Claude Code accounts&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 — Move your accounts to another machine, once
&lt;/h2&gt;

&lt;p&gt;On a new machine you could sign in to every account again. Or you can carry them over in one paste.&lt;/p&gt;

&lt;p&gt;On the machine that has the accounts, open the accounts panel and click &lt;code&gt;↗&lt;/code&gt; in its header. Every account starts ticked; untick the ones that should stay. Click &lt;strong&gt;Share N accounts&lt;/strong&gt;, then &lt;strong&gt;copy all N&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/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk9r9haua5svlyd4uustl.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%2Fk9r9haua5svlyd4uustl.webp" alt="The Share accounts dialog: 3 accounts ready to paste, one line of text starting with ccdeck2:, and a warning that this is 3 passwords that expire in 10 minutes" width="330" height="284"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data. The text shown is not a real share.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On the second machine: accounts panel → &lt;code&gt;+&lt;/code&gt; → &lt;strong&gt;Paste a share&lt;/strong&gt; → paste → &lt;strong&gt;Import&lt;/strong&gt;. Each account gets a result line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;imported&lt;/strong&gt; — this machine did not have it, and now does;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;already here&lt;/strong&gt; — left exactly as it was (there is an &lt;strong&gt;update anyway&lt;/strong&gt; button if you need it);&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dead token replaced&lt;/strong&gt; — claude-swap had marked the login it held as dead, and the share healed it.&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%2Fhx9l0c5so51qncntu3j1.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%2Fhx9l0c5so51qncntu3j1.webp" alt="The import result: 1 of 3 imported, 1 had a dead token replaced, 1 already here" width="330" height="392"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The share carries &lt;strong&gt;logins only&lt;/strong&gt;: no projects, sessions, MCP servers or deck settings. Making it changes nothing on the first machine; both machines now have the accounts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Treat that text as passwords — because it is
&lt;/h3&gt;

&lt;p&gt;This is the part I want every reader to take seriously:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The share is &lt;strong&gt;not encrypted&lt;/strong&gt;. It is claude-swap's export, compressed and base64-encoded. Anyone who has it can use those accounts.&lt;/li&gt;
&lt;li&gt;The deck &lt;strong&gt;refuses to import it after ten minutes&lt;/strong&gt;, but nothing cryptographically enforces that; the tokens inside stay valid after that. A copy can linger in a clipboard history, or sync to your other devices.&lt;/li&gt;
&lt;li&gt;Carry it by a route you would trust with the passwords themselves. If a copy ends up somewhere it should not, sign those accounts out and back in at Anthropic — that is what revokes a login.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full guide: &lt;a href="https://ccdeck.dev/guides/move-an-account/" rel="noopener noreferrer"&gt;Copy your Claude Code login to another machine&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3 — Let your machines repair each other's logins
&lt;/h2&gt;

&lt;p&gt;Step 2 is a one-off. Step 3 is what actually ended my weekly &lt;code&gt;claude login&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At the bottom of the accounts panel there is a &lt;strong&gt;Local network&lt;/strong&gt; row. With it on, decks on the same network find each other, pair, and &lt;strong&gt;a login that has expired on one machine is copied from another machine that still has a working copy&lt;/strong&gt;, about once a minute.&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%2F7f7pxtjk46izavtm0474.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%2F7f7pxtjk46izavtm0474.webp" alt="The Local network view: Sync with paired decks is on, no deck paired yet" width="284" height="228"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What it sends
&lt;/h3&gt;

&lt;p&gt;While the switch is on, the deck announces its name, a key fingerprint and its port &lt;strong&gt;every 30 seconds on UDP port 45317&lt;/strong&gt;, where every machine on the network can hear it. It also listens for other decks on a TCP port of its own. That is worth knowing before you turn it on at a coffee shop — more on that below.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tick what each machine may hand out
&lt;/h3&gt;

&lt;p&gt;Click the sliders button at the top of the Local network view. Under &lt;strong&gt;Share these accounts&lt;/strong&gt;, tick each login this machine may give to a paired machine.&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%2Fjyl6xgiayvtz5ditbp35.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%2Fjyl6xgiayvtz5ditbp35.webp" alt="The " width="330" height="472"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data. Only one of the two accounts is ticked.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Tick each login on the machine that may hand it out. A paired deck can add an account it is missing or repair an expired copy from those shared logins. An account you leave unticked is not offered by this machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pair
&lt;/h3&gt;

&lt;p&gt;As shipped, decks on one network pair on their own within a minute or two. If you turn off &lt;strong&gt;Say yes to every deck that asks&lt;/strong&gt;, a &lt;em&gt;"A deck wants to pair"&lt;/em&gt; dialog appears instead — click &lt;strong&gt;Accept&lt;/strong&gt; only if the fingerprint matches the one the other machine shows under its own name. (Decline has the focus, so a stray Enter shares nothing.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Watch an expired login come back
&lt;/h3&gt;

&lt;p&gt;When a login is rejected on this machine, its row says &lt;strong&gt;Login expired&lt;/strong&gt;. If a paired machine has a working copy and ticks it, the next round copies it over. Click the other deck's row to see what the next round will do:&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%2Fw9pdq6op6hyoa9dlo2ld.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%2Fw9pdq6op6hyoa9dlo2ld.webp" alt="A paired deck's dialog: one account expired here and working on the other machine, marked " width="330" height="366"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Two rules make this safe to leave running:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A working login is never replaced.&lt;/strong&gt; A paired machine can add a missing account or repair a copy that has expired here.&lt;/li&gt;
&lt;li&gt;If every machine's copy has expired, the row says &lt;strong&gt;Login expired on all online decks&lt;/strong&gt;. Sign in again on any one of them, and the next round brings it to the others.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When they cannot see each other
&lt;/h3&gt;

&lt;p&gt;Two subnets, a guest network, a VPN: the UDP announcement does not cross them. Click the chain-link button to open &lt;strong&gt;Add a deck&lt;/strong&gt;, then either type the other deck's address, or &lt;strong&gt;make an invite&lt;/strong&gt; on one machine and paste it on the other. An invite works once and lasts ten minutes; whoever pastes it dials out, so paste it on the machine that cannot be reached from outside.&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%2Fjs7ukess6g91rekjjzvw.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%2Fjs7ukess6g91rekjjzvw.webp" alt="The Add a deck dialog: pair by address, or with an invite" width="330" height="314"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you use &lt;strong&gt;Tailscale&lt;/strong&gt;, the &lt;strong&gt;This deck on the network&lt;/strong&gt; dialog has a &lt;em&gt;Look for my devices&lt;/em&gt; switch that finds decks on your other machines over your tailnet, wherever they are — only machines signed in to your own Tailscale account are asked or answered without a click.&lt;/p&gt;

&lt;p&gt;Full guide: &lt;a href="https://ccdeck.dev/guides/local-network/" rel="noopener noreferrer"&gt;Repair expired Claude Code logins across machines&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The security model, in plain words
&lt;/h2&gt;

&lt;p&gt;Pairing is the decision that matters, so here is what it means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A paired deck can copy &lt;strong&gt;any login you tick&lt;/strong&gt;, and what crosses the wire is the &lt;strong&gt;live credential&lt;/strong&gt;, sealed to the deck it is addressed to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On a network you do not own&lt;/strong&gt;, turn off &lt;em&gt;Say yes to every deck that asks&lt;/em&gt;, or turn on &lt;em&gt;Pair new decks only by invite&lt;/em&gt;. If you never want it, start the deck with &lt;code&gt;AGENTS_DECK_NO_LAN=1&lt;/code&gt; and Local network stays off whatever the switch says.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unpairing and unticking take nothing back.&lt;/strong&gt; They stop future rounds; a login already copied stays where it went. Only signing in again at Anthropic revokes a login everywhere.&lt;/li&gt;
&lt;li&gt;If you remove an account on one machine while a paired machine still ticks it, it can come back on the next round. Untick it on the other machine first.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;What I do now, on every new machine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;npx ccdeck&lt;/code&gt; (or install the app).&lt;/li&gt;
&lt;li&gt;Paste one share from a machine I already use — every account arrives at once.&lt;/li&gt;
&lt;li&gt;Tick the accounts under Local network, on both sides.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After that I stopped thinking about logins. When one expires on the machine I have not touched for a week, it is quietly repaired from the one I used yesterday.&lt;/p&gt;

&lt;ul&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;
&lt;/li&gt;
&lt;li&gt;Guides: &lt;a href="https://ccdeck.dev/guides/" rel="noopener noreferrer"&gt;https://ccdeck.dev/guides/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Licence: AGPL-3.0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try it and something does not work as described, &lt;a href="https://github.com/BarganConstantin/ccdeck/issues" rel="noopener noreferrer"&gt;an issue&lt;/a&gt; is the fastest way to get it fixed.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>openai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Your agent session is a tree, your terminal shows a scroll — watching Claude Code subagents live</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Tue, 13 Oct 2026 10:50:03 +0000</pubDate>
      <link>https://dev.to/bargan/your-agent-session-is-a-tree-your-terminal-shows-a-scroll-watching-claude-code-subagents-live-21en</link>
      <guid>https://dev.to/bargan/your-agent-session-is-a-tree-your-terminal-shows-a-scroll-watching-claude-code-subagents-live-21en</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 key and setting, is at &lt;a href="https://ccdeck.dev/guides/read-a-session/" rel="noopener noreferrer"&gt;ccdeck.dev/guides/read-a-session&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Four subagents, one column of text
&lt;/h2&gt;

&lt;p&gt;I ask Claude Code to add rate limiting to an API. It starts a subagent to write the migration, another to update the docs, a reviewer, and a general-purpose one to chase something it found. Four agents, working at the same time.&lt;/p&gt;

&lt;p&gt;My terminal shows that as a scroll. Every tool call from every subagent arrives in one column, interleaved, and the questions I actually have are the ones a scroll answers worst:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;What is running right now?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;What did that subagent actually do?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Which one is stuck, and on what?&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An agent session is a tree. So I wanted to look at it as a tree.&lt;/p&gt;




&lt;h2&gt;
  
  
  The canvas: one card per agent
&lt;/h2&gt;

&lt;p&gt;ccdeck is a local dashboard for Claude Code and Codex CLI. Start it with:&lt;br&gt;
&lt;/p&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;It opens on &lt;code&gt;http://127.0.0.1:4317&lt;/code&gt; and draws every session on the machine. &lt;strong&gt;Each card is one agent&lt;/strong&gt;: a session, or a subagent that a Claude Code session started. A line joins each subagent to its session. It is dashed and moving while the subagent runs, and solid once it finishes.&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%2Fikhm9q8s4g29qipu3235.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%2Fikhm9q8s4g29qipu3235.webp" alt="One session with four subagents: three marked DONE on solid lines, the reviewer still LIVE on a dashed line, with tool bubbles beside every card" width="800" height="359"&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;Here is how to read a card:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The state pill&lt;/strong&gt; reads &lt;code&gt;LIVE&lt;/code&gt;, &lt;code&gt;DONE&lt;/code&gt; or &lt;code&gt;ERR&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The bubbles&lt;/strong&gt; to the right are the card's last four tool calls: a spinner while one runs, ✓ when it finished, × when it failed. Beside a &lt;code&gt;Bash&lt;/code&gt; call, a second bubble names the program it ran (such as &lt;code&gt;npm&lt;/code&gt;); beside a file call, the file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The top row&lt;/strong&gt; has the name and how long it has run. A session is named after its folder, a subagent after its type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;→ 4&lt;/code&gt;&lt;/strong&gt; on a session means it started four subagents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The model chip&lt;/strong&gt; says which model is doing the work, such as &lt;code&gt;Opus 5&lt;/code&gt; or &lt;code&gt;Sonnet 5&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The last row&lt;/strong&gt; counts tool calls, those still in flight, and failures once there is one. A session adds its input and output tokens and its spend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The ring&lt;/strong&gt; before a session's clock is the share of the model's context window in use. For Claude Code that is the deck's estimate; for Codex it is Codex's own figure. Click it for the breakdown.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Codex sessions land on the same canvas, told apart by an OpenAI model chip. They show less: Codex's log records no subagents and no title, so a Codex card has no subagent cards under it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting around
&lt;/h2&gt;

&lt;p&gt;The keys I use the most:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;F&lt;/code&gt;&lt;/strong&gt; fits every agent on screen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;J&lt;/code&gt; / &lt;code&gt;K&lt;/code&gt;&lt;/strong&gt; go to the next and previous agent and open its details.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Z&lt;/code&gt;&lt;/strong&gt; zooms to the selected agent and its session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;L&lt;/code&gt;&lt;/strong&gt; opens the session list; &lt;strong&gt;&lt;code&gt;D&lt;/code&gt;&lt;/strong&gt; the detail panel; &lt;strong&gt;&lt;code&gt;Esc&lt;/code&gt;&lt;/strong&gt; closes what is open.&lt;/li&gt;
&lt;li&gt;Drag the empty canvas or scroll to pan, and hold &lt;code&gt;Ctrl&lt;/code&gt; (&lt;code&gt;Cmd&lt;/code&gt; on macOS) and scroll to zoom.&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%2F07uf39vtkem4yn1tbphe.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%2F07uf39vtkem4yn1tbphe.webp" alt="The canvas controls in the bottom left, and the " width="776" height="560"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data. The chip appears once you move the view.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once you move the view or click a card, the deck stops pulling new sessions into view and says &lt;strong&gt;Auto-fit off&lt;/strong&gt;. &lt;strong&gt;Resume&lt;/strong&gt; turns it back on. &lt;code&gt;F&lt;/code&gt; fits once and leaves auto-fit off.&lt;/p&gt;

&lt;p&gt;The session list (&lt;code&gt;L&lt;/code&gt;) puts sessions waiting on you first, then running ones, then finished ones. A row counts its subagents' tool calls too. Clicking a row frames that session and opens its detail 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%2Fmivpwdl5kbze24am7v5o.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%2Fmivpwdl5kbze24am7v5o.webp" alt="The session list next to a zoomed-out canvas, where cards become small faces" width="800" height="364"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data. Zoomed far out, cards become small faces; hover one for its name, state and numbers.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What did that subagent actually do?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Double-click a card.&lt;/strong&gt; (From the keyboard: &lt;code&gt;Tab&lt;/code&gt; to the card, then &lt;code&gt;Enter&lt;/code&gt;.) Its detail panel opens with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Activity&lt;/strong&gt;: how many calls, how many are running or failed, and the calls by kind;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tokens&lt;/strong&gt;: in, out, cache read and cache created;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identity&lt;/strong&gt;: the folder, the session id and, for a subagent, its parent;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompts&lt;/strong&gt;: what was typed into the session, newest first;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool calls&lt;/strong&gt;: every call, newest first, with how long it took.&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%2F9zdp9ei7kcqlgae52p4i.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%2F9zdp9ei7kcqlgae52p4i.webp" alt="The detail panel for a session: state, model, spend, token counts, the prompt " width="720" height="1880"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data. The newest call is still running, so it shows … instead of a duration.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Tokens and spend belong to the session. A Claude Code session's figures include its subagents, so a subagent's own panel has no tokens, spend or prompts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then click any tool call.&lt;/strong&gt; A dialog shows its &lt;strong&gt;Input&lt;/strong&gt; and its &lt;strong&gt;Response&lt;/strong&gt;, each with a copy button: for &lt;code&gt;Bash&lt;/code&gt; the command and its output, for &lt;code&gt;Edit&lt;/code&gt; the file and the change, for &lt;code&gt;Read&lt;/code&gt; the file. Clicking a bubble on the canvas opens the same dialog.&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%2Fsqi6qnca7il8d8ufo893.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%2Fsqi6qnca7il8d8ufo893.webp" alt="A Bash tool call: the command npm test -- store, and its output with two passing tests" width="800" height="353"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data. A finished Bash call from the migrator subagent.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is the part I use the most. It reports &lt;strong&gt;what actually executed&lt;/strong&gt;, from the hook stream, not what the agent said it did in its summary.&lt;/p&gt;




&lt;h2&gt;
  
  
  It survives a restart
&lt;/h2&gt;

&lt;p&gt;The deck appends every event to a log and reads back up to the newest 2,000 when it starts. A restart — after an upgrade, or a reboot — redraws the sessions that were there.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A card with &lt;code&gt;?&lt;/code&gt; beside its name and &lt;code&gt;≥&lt;/code&gt; before its clock is a session the deck joined late; its clock counts from when the deck first saw it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npx ccdeck --status&lt;/code&gt; names each running deck's log file. &lt;code&gt;--history &amp;lt;path&amp;gt;&lt;/code&gt; uses a file of your choosing, and &lt;code&gt;--no-persist&lt;/code&gt; writes and replays nothing.&lt;/li&gt;
&lt;li&gt;Clearing the canvas (&lt;code&gt;C&lt;/code&gt;, or the bin in the controls) empties the log too, and asks first.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nothing is recorded while the deck is stopped.&lt;/strong&gt; The hook forwards events to a running deck; if none is running, those events are gone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It keeps the last 200 calls of each agent&lt;/strong&gt;, and the full input and response of only the newest 25. For older calls the dialog shows a short preview and says the full payloads were released.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The board keeps six finished sessions&lt;/strong&gt; and lets an older one go two minutes after it finishes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codex has no subagent cards and no title&lt;/strong&gt;, because its rollout log does not record them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It watches; it does not steer.&lt;/strong&gt; The Claude Code hook only forwards events, so the deck cannot allow, deny or change anything an agent does.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try it
&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;Then ask Claude Code for something that fans out into subagents, and watch the tree grow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Guide: &lt;a href="https://ccdeck.dev/guides/read-a-session/" rel="noopener noreferrer"&gt;https://ccdeck.dev/guides/read-a-session/&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;
&lt;/li&gt;
&lt;li&gt;Licence: AGPL-3.0&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>openai</category>
      <category>claude</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Which Claude Code session is waiting on you? A queue for agents that stopped to ask</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Fri, 09 Oct 2026 05:27:18 +0000</pubDate>
      <link>https://dev.to/bargan/which-claude-code-session-is-waiting-on-you-a-queue-for-agents-that-stopped-to-ask-1ok9</link>
      <guid>https://dev.to/bargan/which-claude-code-session-is-waiting-on-you-a-queue-for-agents-that-stopped-to-ask-1ok9</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/waiting-on-you/" rel="noopener noreferrer"&gt;ccdeck.dev/guides/waiting-on-you&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  One silent prompt among many tabs
&lt;/h2&gt;

&lt;p&gt;You start four Claude Code sessions in four repositories, switch to something else, and come back twenty minutes later. Three of them are done. The fourth stopped two minutes after you left, on a permission prompt for a &lt;code&gt;Bash&lt;/code&gt; command, and has been sitting there since.&lt;/p&gt;

&lt;p&gt;Nothing told you. From the outside every terminal tab looks the same — the one that is working and the one that is waiting. You find the stuck one by clicking through them, and it is usually the one that was closest to finished.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The scarce resource is not tokens. It is your attention&lt;/strong&gt;, and the cost is the time an agent spends waiting for you without you knowing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Code already knows — it tells nobody
&lt;/h2&gt;

&lt;p&gt;Claude Code has a hook event for exactly this: &lt;strong&gt;&lt;code&gt;Notification&lt;/code&gt;&lt;/strong&gt;. It fires when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Code wants to use a tool and is waiting for your permission;&lt;/li&gt;
&lt;li&gt;the agent stopped to ask you a question;&lt;/li&gt;
&lt;li&gt;a turn finished and the session has been idle, waiting for your next instruction (Claude Code reports that about a minute after the turn ends).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A hook can listen for it. The problem is that a hook in each terminal still talks to only that terminal. What you want is &lt;strong&gt;one place that collects them all&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;npx ccdeck&lt;/code&gt;
&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;On its first run, ccdeck adds a hook to &lt;code&gt;~/.claude/settings.json&lt;/code&gt; for the &lt;code&gt;Notification&lt;/code&gt; event and nine others. Each event is POSTed to the deck on &lt;code&gt;127.0.0.1&lt;/code&gt;, and the deck opens at &lt;code&gt;http://127.0.0.1:4317&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The hook &lt;strong&gt;cannot steer your agent&lt;/strong&gt;. Claude Code's hook protocol gives a hook two channels to allow, deny or rewrite a tool call — the exit code and stdout — and this one always exits &lt;code&gt;0&lt;/code&gt; and prints nothing. It forwards; it never answers.&lt;/p&gt;

&lt;p&gt;(Install details, the desktop app, and every option are in the &lt;a href="https://ccdeck.dev/guides/first-run/" rel="noopener noreferrer"&gt;first-run guide&lt;/a&gt;.)&lt;/p&gt;

&lt;h2&gt;
  
  
  The queue
&lt;/h2&gt;

&lt;p&gt;Press &lt;code&gt;L&lt;/code&gt; to open the session list. A session stopped on you moves to the top, and its run time is replaced by &lt;strong&gt;how long it has been waiting&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/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7cw5qgo7tkmgu73udt7r.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%2F7cw5qgo7tkmgu73udt7r.webp" alt="The session list: 4 sessions, 2 waiting. web-api waiting 6m on Bash, docs-site waiting 2m on Write, data-pipeline waiting 12m, and infra running for 13m" width="480" height="744"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The order is deliberate:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Permission prompts and questions first&lt;/strong&gt; — the agent cannot go on at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Then finished turns&lt;/strong&gt; waiting for your next instruction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Within each group, the longest wait first.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So a turn that finished 12 minutes ago sits below a prompt from 2 minutes ago, because the prompt is the one actually blocking work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jump straight to the oldest
&lt;/h2&gt;

&lt;p&gt;The topbar shows an amber count of sessions blocked on a prompt or a question:&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%2Fvgt2zq77gk9o5xeqg065.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%2Fvgt2zq77gk9o5xeqg065.webp" alt="The topbar: this month's tokens and spend, then " width="656" height="104"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Click it, or press &lt;strong&gt;&lt;code&gt;W&lt;/code&gt;&lt;/strong&gt;: the deck selects the session that has waited longest, brings its card on screen and opens its detail panel. Each further &lt;code&gt;W&lt;/code&gt; goes to the next one.&lt;/p&gt;

&lt;p&gt;While the count is above zero, the browser tab title reads &lt;strong&gt;&lt;code&gt;(2) ccdeck&lt;/code&gt;&lt;/strong&gt; and its icon turns into an amber disc, so you can see it from any other tab.&lt;/p&gt;

&lt;p&gt;On the card itself, an amber row holds the start of Claude Code's own sentence and a clock:&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%2Fhefr6vh75nzenu8a4mjs.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%2Fhefr6vh75nzenu8a4mjs.webp" alt="The web-api card with an amber row: " width="748" height="468"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For a finished turn the row reads &lt;strong&gt;Your turn&lt;/strong&gt; instead. Either way, you answer in the terminal where Claude Code is running, and the row goes away as soon as the session moves again.&lt;/p&gt;

&lt;h2&gt;
  
  
  When you are not looking at the page
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sounds.&lt;/strong&gt; &lt;code&gt;V&lt;/code&gt; opens the Sound menu: one tone when a turn finishes, another when Claude is asking. &lt;code&gt;M&lt;/code&gt; mutes from anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notifications.&lt;/strong&gt; Switch on &lt;strong&gt;Notifications while closed&lt;/strong&gt; in the same menu. With the deck's tab hidden, each new prompt or question raises a browser notification; with no deck tab open at all, you get a system notification instead. Click it to land on that session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The desktop app&lt;/strong&gt; puts the count in the menu bar on macOS (in the tray tooltip and menu on Windows and Linux), so you do not need a tab open at all.&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%2F3jmad3ekneeom0vdq2ck.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%2F3jmad3ekneeom0vdq2ck.webp" alt="The Sound menu: Sounds and Notifications while closed both on, with a tone and volume for " width="568" height="1008"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Demo data.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I would rather you know the limits before you install it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It cannot answer for you.&lt;/strong&gt; The hook only forwards, so the deck cannot allow, deny or reply. You still switch to the terminal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code only.&lt;/strong&gt; Codex CLI is drawn on the same canvas, but the deck reads Codex from its rollout log, and that log records no approval request, so a Codex session is never marked waiting. (Codex's &lt;code&gt;app-server&lt;/code&gt; does report a waiting status; reading it is open work.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The tool name is a guess.&lt;/strong&gt; "waiting 6m · Bash" names the newest call still running when the prompt arrived, if it started within 30 seconds of it. Claude Code does not report it. Hover the time for Claude Code's own sentence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ninety minutes.&lt;/strong&gt; A session that sends nothing for 90 minutes stops showing as waiting.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&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;Then start a Claude Code session, ask it to run something that needs permission, and switch away. When the prompt appears, the session moves to the top of the list with a clock beside it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Guide: &lt;a href="https://ccdeck.dev/guides/waiting-on-you/" rel="noopener noreferrer"&gt;https://ccdeck.dev/guides/waiting-on-you/&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>claude</category>
      <category>githubcopilot</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
