<?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: EdgeStorage</title>
    <description>The latest articles on DEV Community by EdgeStorage (@edgestorage).</description>
    <link>https://dev.to/edgestorage</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%2F4124950%2Ff3cc2b5b-1b2c-49dd-a192-cf96ba2c923f.jpg</url>
      <title>DEV Community: EdgeStorage</title>
      <link>https://dev.to/edgestorage</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/edgestorage"/>
    <language>en</language>
    <item>
      <title>Codex 在 Windows + PowerShell 上的问题，我们决定换个思路绕开</title>
      <dc:creator>EdgeStorage</dc:creator>
      <pubDate>Wed, 14 Oct 2026 07:49:34 +0000</pubDate>
      <link>https://dev.to/edgestorage/codex-zai-windows-powershell-shang-de-wen-ti-wo-men-jue-ding-huan-ge-si-lu-rao-kai-leb</link>
      <guid>https://dev.to/edgestorage/codex-zai-windows-powershell-shang-de-wen-ti-wo-men-jue-ding-huan-ge-si-lu-rao-kai-leb</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;标题备选（数字钩子，需带上查询语句与日期）：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;「4,269 条 issue 标题里带 Windows：Codex 在 PowerShell 下的问题是结构性的」&lt;/li&gt;
&lt;li&gt;「Codex CLI 有 203 条标题含 PowerShell 的 issue，我们不再指望修好它」&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;先说结论：Codex 在 Windows 上不是不能用，而是&lt;strong&gt;你把任务规模化之后，被 PowerShell 打断的次数会变成一种固定成本&lt;/strong&gt;。我们试过的方向里，性价比最高的不是写更长的提示词教模型写 PowerShell，而是把执行面换到 Linux 用户态。&lt;/p&gt;

&lt;p&gt;这篇先把网上能查到的真实问题摊开（都带链接，可以自己核），再讲我们在 TaskHandoff 里选的那条路，以及这条路的代价。&lt;/p&gt;

&lt;h2&gt;
  
  
  一、先看真实问题：四类，且不是同一类
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A 类：PowerShell 不是 Bash，模型的命令会持续撞墙
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Codex CLI 在 PowerShell 下会&lt;strong&gt;反复生成&lt;/strong&gt; &lt;code&gt;rg 'pattern' *.md&lt;/code&gt; 这种写法，直接报 &lt;code&gt;os error 123&lt;/code&gt;；同一环境里 &lt;code&gt;rg -g '*.md' 'pattern' .&lt;/code&gt; 是正常的。报告者的原话是：这不是一次失败，而是在正常使用中不断生成不被支持的命令形式，产生可避免的失败调用和恢复工作，浪费时间和 token（&lt;a href="https://github.com/openai/codex/issues/45685" rel="noopener noreferrer"&gt;#45685&lt;/a&gt;）。&lt;/li&gt;
&lt;li&gt;过大的 PowerShell 命令会在启动前就失败，报 &lt;code&gt;os error 206&lt;/code&gt;（&lt;a href="https://github.com/openai/codex/issues/45788" rel="noopener noreferrer"&gt;#45788&lt;/a&gt;）。&lt;/li&gt;
&lt;li&gt;引号、反斜杠、JSON 参数被 PowerShell 改写；从 Bash 教程抄来的命令直接失败；&lt;code&gt;ExecutionPolicy&lt;/code&gt; 拦截脚本；路径带空格或中文就出问题（&lt;a href="https://knightli.com/2026/07/29/codex-windows-native-wsl-sandbox-path-troubleshooting/" rel="noopener noreferrer"&gt;knightli.com 的排查手册&lt;/a&gt;）。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  B 类：Windows 特有的策略与沙箱层
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;exec_command&lt;/code&gt; 在 PowerShell 还没启动前就被拒绝：&lt;code&gt;CreateProcess rejected: blocked by policy&lt;/code&gt;，没有子进程 PID、没有退出码。值得注意的是，报告者当时已经配置 &lt;code&gt;approval_policy=Never&lt;/code&gt; + &lt;code&gt;sandbox_policy=DangerFullAccess&lt;/code&gt;，依然被拦，而且错误信息&lt;strong&gt;不告诉你是哪一层策略拦的&lt;/strong&gt;（&lt;a href="https://github.com/openai/codex/issues/44839" rel="noopener noreferrer"&gt;#44839&lt;/a&gt;）。同类报告还有只读 &lt;code&gt;Get-Content&lt;/code&gt; 被拒（&lt;a href="https://github.com/openai/codex/issues/43881" rel="noopener noreferrer"&gt;#43881&lt;/a&gt;）、只读健康检查被拒（&lt;a href="https://github.com/openai/codex/issues/43633" rel="noopener noreferrer"&gt;#43633&lt;/a&gt;）。&lt;/li&gt;
&lt;li&gt;Windows 沙箱阻塞 Node 子进程：&lt;code&gt;spawnSync EPERM&lt;/code&gt;、&lt;code&gt;errno -4048&lt;/code&gt;；想改成提权模式，反而在 UAC 提示出现前就失败：&lt;code&gt;helper_sandbox_lock_failed&lt;/code&gt; / &lt;code&gt;SetNamedSecurityInfoW ... failed: 5&lt;/code&gt;（&lt;a href="https://github.com/openai/codex/issues/45697" rel="noopener noreferrer"&gt;#45697&lt;/a&gt;）。同类还有子进程管道建立失败、PID 为 0（&lt;a href="https://github.com/openai/codex/issues/44838" rel="noopener noreferrer"&gt;#44838&lt;/a&gt;）。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  C 类：跨文件系统与路径映射
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;切到 WSL2 之后，项目路径会变成 &lt;code&gt;/mnt/c/Program Files/WindowsApps/...&lt;/code&gt; 这种形态；合法符号链接被拒；粘贴的图片 image tool 读不到（而沙箱里的 Bash 明明能读）（&lt;a href="https://github.com/openai/codex/issues/44182" rel="noopener noreferrer"&gt;#44182&lt;/a&gt;）。Windows + WSL2 无法创建本地项目也有人在报（&lt;a href="https://github.com/openai/codex/issues/45887" rel="noopener noreferrer"&gt;#45887&lt;/a&gt;）。&lt;/li&gt;
&lt;li&gt;CRLF 会让 Git 显示「所有文件都被修改」，权限位全部变化；同一个仓库可能出现 Windows 侧能拉取、WSL 侧不能拉取（knightli.com）。&lt;/li&gt;
&lt;li&gt;官方说明里有一条容易被忽略：&lt;strong&gt;集成终端 shell 和 agent environment 是两个独立设置&lt;/strong&gt;。你把集成终端改成 Git Bash，agent 依然用 PowerShell 跑命令（&lt;a href="https://nenufm.com/posts/optimize-codex-shell-on-windows" rel="noopener noreferrer"&gt;nenufm.com&lt;/a&gt;）。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  D 类：安全软件与企业环境
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;卡巴斯基把 &lt;code&gt;%USERPROFILE%\.codex\.sandbox-bin\codex-command-runner-0.154.0.exe&lt;/code&gt; 判为 &lt;code&gt;PDM:Exploit.Win32.Generic.nblk&lt;/code&gt; 并拦截；而该文件与官方 npm 包内的文件 SHA-256 完全一致，Kaspersky 自己的动态分析结果又是 Clean（&lt;a href="https://github.com/openai/codex/issues/45810" rel="noopener noreferrer"&gt;#45810&lt;/a&gt;）。&lt;/li&gt;
&lt;li&gt;与 FSLogix 不兼容（&lt;a href="https://github.com/openai/codex/issues/45897" rel="noopener noreferrer"&gt;#45897&lt;/a&gt;）；应用商店更新后启动失败报 &lt;code&gt;rename_staging EPERM&lt;/code&gt;（&lt;a href="https://github.com/openai/codex/issues/45858" rel="noopener noreferrer"&gt;#45858&lt;/a&gt;）。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;规模参考&lt;/strong&gt;（2026-09-16 用 GitHub 搜索查询，计数为近似值）：&lt;code&gt;repo:openai/codex type:issue&lt;/code&gt; 共 28,341 条，其中 &lt;code&gt;label:"windows-os"&lt;/code&gt; 7,291 条，标题含 Windows 的 issue 4,269 条，标题含 PowerShell 的 203 条。&lt;/p&gt;

&lt;h2&gt;
  
  
  二、为什么「修好 PowerShell」这条路收益有限
&lt;/h2&gt;

&lt;p&gt;把上面四类分开看就清楚了：A 类是 shell 语义差异，B 类是 Windows 安全模型与沙箱实现，C 类是跨文件系统映射，D 类是第三方安全软件。&lt;strong&gt;它们不是一个补丁能一起解决的问题。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;社区里有人做过一轮很扎实的尝试（&lt;a href="https://nenufm.com/posts/optimize-codex-shell-on-windows" rel="noopener noreferrer"&gt;nenufm.com&lt;/a&gt;，2026-07-21 发布、2026-09-06 更新）：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;装了 PowerShell 7，对体验有帮助，保留；&lt;/li&gt;
&lt;li&gt;把一大段 PowerShell 工具提示词放进全局 &lt;code&gt;AGENTS.md&lt;/code&gt;——&lt;strong&gt;没解决问题&lt;/strong&gt;，后来删掉了；&lt;/li&gt;
&lt;li&gt;装了 Coreutils 想让命令更接近 Unix，结果 &lt;code&gt;PSConsoleHostReadLine&lt;/code&gt; 会改写交互输入，&lt;code&gt;Get-Command rm&lt;/code&gt; 显示的别名和实际执行入口不一致，辨认成本上升，最后卸载；&lt;/li&gt;
&lt;li&gt;还发现失败未必跟 shell 语法有关：有一次是 Codex 调用了旧版本目录里的 &lt;code&gt;codex.exe&lt;/code&gt;，换到当前安装路径就成功了——这种错误升级 PowerShell 或换 Bash 都救不了。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;他最后的做法是把约束写简单：精确搜索用 &lt;code&gt;rg -n -F&lt;/code&gt;、多条件用多个 &lt;code&gt;-e&lt;/code&gt;、不要额外嵌套 &lt;code&gt;pwsh -Command&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;这条经验值得记住：&lt;strong&gt;提示词能降低概率，但降不到零；而每次失败的代价是一次工具调用、一次恢复、以及被消耗掉的上下文。&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  三、我们选的路：Windows 只当入口，任务跑在 Linux 侧
&lt;/h2&gt;

&lt;p&gt;TaskHandoff 在 Windows 桌面端上有一个明确的约束：&lt;strong&gt;Windows 上创建本机实例走的是 Docker Runtime，内置 Local Runtime 只在受支持的非 Windows 节点提供&lt;/strong&gt;（见 &lt;a href="https://docs.thandoff.com/guide/install" rel="noopener noreferrer"&gt;安装与登录&lt;/a&gt;）。&lt;/p&gt;

&lt;p&gt;翻译成使用方式就是：Windows 负责界面，任务实际跑在 Linux 容器里。A 类的 PowerShell 语义问题、B 类的 Windows 沙箱策略问题，在这一层就不存在了——agent 执行的是容器内 Linux shell 的命令。&lt;/p&gt;

&lt;p&gt;另外两条路径也在同一套模型里：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;把远程 Linux 主机加为节点。&lt;/strong&gt; 一台 Debian/Ubuntu 机器加入后，和本机实例出现在同一个控制面里，可以按节点、项目、状态筛选。装法和步骤见&lt;a href="https://docs.thandoff.com/guide/first-setup" rel="noopener noreferrer"&gt;首次配置&lt;/a&gt;和&lt;a href="https://docs.thandoff.com/integrations/remote-nodes" rel="noopener noreferrer"&gt;远程节点&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;只让 Windows 承担操作入口。&lt;/strong&gt; 桌面端、浏览器、移动端、聊天桥接共用同一个控制面，跟进任务不需要守在一个 PowerShell 窗口前。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;顺带解决掉几个 C 类和运维层面的老问题：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;路径与工作目录&lt;/strong&gt;：一次 AI 会话绑定固定工作目录，创建后不会自动改变；要换 worktree，就在目标 worktree 上新建会话（见&lt;a href="https://docs.thandoff.com/sessions/" rel="noopener noreferrer"&gt;使用 AI 会话&lt;/a&gt;）。少一层「Windows 路径 → WSL 路径 → 容器路径」的心智转换。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;交付&lt;/strong&gt;：改动通过文件/变更视图审阅，走分支或 worktree 交付；远端操作是保守的——Pull 仅允许 fast-forward，&lt;strong&gt;永远不 force push&lt;/strong&gt;，分支分叉就阻止自动同步（见&lt;a href="https://docs.thandoff.com/workspace/repository" rel="noopener noreferrer"&gt;仓库协作&lt;/a&gt;）。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;凭据&lt;/strong&gt;：Git 凭据按「协议 + 主机 + 端口 + 大小写敏感的路径前缀」限定范围，不支持通配符；HTTPS token 只在 credential-helper 请求期间短暂提供，不写进 remote URL、命令行和环境变量（见&lt;a href="https://docs.thandoff.com/security/git-credentials" rel="noopener noreferrer"&gt;Git 凭据&lt;/a&gt;）。不用在 Windows 上给每个项目单独配一遍。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;长任务不靠窗口活着&lt;/strong&gt;：关闭桌面主窗口只是隐藏到托盘，Docker Runtime 实例保持运行，下次启动后节点代理会重新发现并接管（见&lt;a href="https://docs.thandoff.com/guide/install" rel="noopener noreferrer"&gt;安装与登录&lt;/a&gt;）。配合移动端和聊天桥接，离开电脑也能看进度。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  四、代价，说清楚
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Windows 上要装 Docker。&lt;/strong&gt; 安装桌面应用本身不等于安装 Docker；创建 Docker 实例前，节点必须已安装并启动 Docker daemon，且当前用户有权限访问它。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local Runtime 在 Windows 上不可用&lt;/strong&gt;，想要「直接使用宿主机目录和工具」的用法得放到非 Windows 节点上。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;换成 Linux 用户态意味着接受 Linux 工具链&lt;/strong&gt;。如果你的项目强依赖 Windows 专有工具，这套方式不适用。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;多了一层容器&lt;/strong&gt;：镜像拉取、卷管理和环境模板这些概念需要理解一次。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  五、可复用的五条原则
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;区分「操作环境」和「执行环境」。&lt;/strong&gt; 你人在 Windows 上工作，不代表 agent 必须在 PowerShell 里执行命令。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;不要在提示词层面解决平台语义问题。&lt;/strong&gt; 提示词能降低概率，不能替代把环境换对。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;同一类问题要分层归因&lt;/strong&gt;：shell 语义、沙箱策略、文件系统映射、安全软件——它们的解法完全不同。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;让失败可解释。&lt;/strong&gt; 「blocked by policy」这种没有规则归属的错误，会让 agent 和人都无法自行恢复；选工具时要把它算作一个真实成本。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;长任务的存活不要依赖终端窗口。&lt;/strong&gt; 判定方法：关掉窗口、重启界面服务，任务还在不在跑。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  相关文档
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;安装与平台支持（含 Windows 使用 Docker Runtime 的说明）：&lt;a href="https://docs.thandoff.com/guide/install" rel="noopener noreferrer"&gt;https://docs.thandoff.com/guide/install&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;首次配置：节点、环境、模型 Provider：&lt;a href="https://docs.thandoff.com/guide/first-setup" rel="noopener noreferrer"&gt;https://docs.thandoff.com/guide/first-setup&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;远程节点接入：&lt;a href="https://docs.thandoff.com/integrations/remote-nodes" rel="noopener noreferrer"&gt;https://docs.thandoff.com/integrations/remote-nodes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;会话与工作目录：&lt;a href="https://docs.thandoff.com/sessions/" rel="noopener noreferrer"&gt;https://docs.thandoff.com/sessions/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;仓库协作与交付：&lt;a href="https://docs.thandoff.com/workspace/repository" rel="noopener noreferrer"&gt;https://docs.thandoff.com/workspace/repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Git 凭据范围管理：&lt;a href="https://docs.thandoff.com/security/git-credentials" rel="noopener noreferrer"&gt;https://docs.thandoff.com/security/git-credentials&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>windows</category>
      <category>selfhosted</category>
      <category>devtools</category>
    </item>
    <item>
      <title>停掉控制面之后，AI 任务还在跑：我们为什么把它拆成三层</title>
      <dc:creator>EdgeStorage</dc:creator>
      <pubDate>Wed, 14 Oct 2026 07:49:27 +0000</pubDate>
      <link>https://dev.to/edgestorage/ting-diao-kong-zhi-mian-zhi-hou-ai-ren-wu-huan-zai-pao-wo-men-wei-shi-yao-ba-ta-chai-cheng-san-ceng-4hdp</link>
      <guid>https://dev.to/edgestorage/ting-diao-kong-zhi-mian-zhi-hou-ai-ren-wu-huan-zai-pao-wo-men-wei-shi-yao-ba-ta-chai-cheng-san-ceng-4hdp</guid>
      <description>&lt;p&gt;用一个界面对着某台机器上的某个终端窗口，是大多数人开始用 AI 写代码的方式。任务变多之后会出现三个具体问题：任务分散在多台机器上、每个任务需要独立且可丢弃的环境、长任务不能因为浏览器关了或者界面断了就死掉。&lt;/p&gt;

&lt;p&gt;这篇讲 TaskHandoff 在这三个问题上做的架构选择：为什么控制面和执行面要拆开，状态到底归谁管，以及由此付出的代价。&lt;/p&gt;

&lt;h2&gt;
  
  
  问题：任务一多，你就开始手动当调度器
&lt;/h2&gt;

&lt;p&gt;假设你手上有三台机器：一台笔记本、一台常开的家用服务器、一台公司内网主机。你在它们上面分别跑 Codex 或 OpenCode。于是你每天在做这些事：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;记得哪台机器上有哪个仓库、哪个分支、哪个 worktree；&lt;/li&gt;
&lt;li&gt;手动把环境装好，或者忍受容器里从零开始装工具；&lt;/li&gt;
&lt;li&gt;反复切 SSH 窗口看任务进度，因为任务只在那个终端的上下文里存在；&lt;/li&gt;
&lt;li&gt;关掉笔记本之后，不知道家用服务器上的任务到底还在不在跑。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这些动作里没有一个是真正的开发工作，但它们都会消耗注意力，而且出错时很难回溯。&lt;/p&gt;

&lt;h2&gt;
  
  
  我们的做法：三层，各管一件事
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;浏览器 / 桌面端 / 移动端 / 聊天平台
              │
              ▼
        Control Plane（UI、API、聊天网关）
              │
              ▼
         Node Agent（节点资源与实例生命周期）
              │
              ▼
     Controlled Instance（工作区、应用、AI 会话）
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;关键不在分层本身，而在&lt;strong&gt;每层各自拥有什么状态&lt;/strong&gt;：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Control Plane&lt;/strong&gt; 拥有节点清单、实例看板、跨实例的 AI 会话视图、聊天凭据与消息路由。它是你操作的地方，也是多端共用的入口。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node Agent&lt;/strong&gt; 拥有节点本地配置、运行时资源、目录清单和实例生命周期。实例的启停由它执行。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Controlled Instance&lt;/strong&gt; 承载工作区、应用、AI 会话和触发器，可以独立运行。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;因为实例生命周期归 Node Agent，&lt;strong&gt;停止或重启 Control Plane 不会顺带停掉节点上由 Node Agent 管理的 Docker 实例&lt;/strong&gt;；控制面板恢复后会重新同步状态。&lt;/p&gt;

&lt;p&gt;同理，AI 会话的对话状态以 AI Session 本身为准，控制面负责的是凭据、绑定、命令解析和路由。所以关掉网页不会中断任务——这条被写进了文档 FAQ，因为它是最常被问的问题。&lt;/p&gt;

&lt;p&gt;一篇会话绑定固定工作目录，创建后不会自动改变（这条也在文档里）。要换个 worktree 干活，是「在目标 worktree 上新建会话」，而不是「把当前会话挪过去」。这是同一套状态所有权思路的延续：会话不追着环境跑。&lt;/p&gt;

&lt;h2&gt;
  
  
  停掉控制面之后会发生什么
&lt;/h2&gt;

&lt;p&gt;这条不用实测也能从状态所有权推出来，文档里也已经把它写成了 FAQ——因为问的人最多：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;关掉浏览器页面&lt;/strong&gt;：任务继续跑，重新打开后恢复权威状态。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;停止或重启 Control Plane&lt;/strong&gt;：由 Node Agent 管理的 Docker 实例继续跑，控制面板恢复后重新同步。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;真正会中断任务的操作&lt;/strong&gt;：停止或删除实例、关闭会话、停止 Node Agent。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这里有个最容易误判的坑，我们也把它写进了文档：&lt;strong&gt;页面显示「正在重连」不等于后台任务停了。&lt;/strong&gt; 如果界面一直卡在重连，问题通常在网络路径上——最常见的原因是反向代理没有转发 &lt;code&gt;/api/events&lt;/code&gt; 的 WebSocket，而不是 Agent 挂了。把「显示状态」和「权威状态」分开看，能省掉不少误判。&lt;/p&gt;

&lt;h2&gt;
  
  
  取舍与被否决的方案
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;为什么不把实例生命周期直接放在控制面？&lt;/strong&gt; 那样控制面重启就会波及正在跑的长任务，而且单点变成了「UI 挂掉 = 所有任务挂掉」。把生命周期的所有权下移到节点，代价是控制面离线期间你只能在节点上继续跑已经存在的实例——新建实例、改配置这类操作都要等控制面回来。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;断线重连的语义要写清楚。&lt;/strong&gt; 界面断线重连时会重新获取权威会话快照。最容易造成误解的是那个「正在重连」提示——它不代表后台任务已经停止。我们把这条明确写进文档，而不是靠用户自己猜：会话状态在 Agent 那边，界面的连接状态只是显示层的问题。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;运行时不锁死在 Docker。&lt;/strong&gt; Docker Runtime 是当前主要的隔离运行时，一个节点可以跑多个实例；Local Runtime 直接用宿主机环境，同一宿主用户只能有一个本地实例，也不支持 Windows。两者共享同一套实例模型，运行时通过 adapter 扩展，所以将来接 Kubernetes 不需要另做一套 UI 流程。&lt;/p&gt;

&lt;h2&gt;
  
  
  结果：行为上发生了什么变化
&lt;/h2&gt;

&lt;p&gt;不写数字，只列可验证的行为差异：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;关掉网页、甚至停掉控制面，节点上正在跑的任务不受影响；重新打开后能拿回权威状态。&lt;/li&gt;
&lt;li&gt;多台机器上的实例出现在同一个看板里，可以按节点、项目、状态筛选，不用逐个 SSH 上去看。&lt;/li&gt;
&lt;li&gt;长任务可以在移动端或聊天里跟进；Telegram、微信、钉钉、飞书的桥接把消息和审批路由到指定实例的会话。&lt;/li&gt;
&lt;li&gt;实例可以独立运行，节点负责它的生命周期，控制面挂了也不会连带停掉它。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  可复用的四条原则
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;把生命周期所有权放在离资源最近的那一层。&lt;/strong&gt; 编排层可以崩、可以升级，但它不该拥有「谁在运行」这个事实。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;区分「显示状态」和「权威状态」。&lt;/strong&gt; 界面上的连接状态、重连提示，都不等于后台任务的状态；接错这两个概念会带来大量虚假告警和误判。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;让长任务的存活不依赖交互层。&lt;/strong&gt; 判定标准很简单：关掉浏览器、重启 UI 服务，任务还在不在跑。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;状态所有权写进文档，而不是留在实现里。&lt;/strong&gt; 用户遇到「正在重连」时，需要一句话告诉他这意味着什么。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  想自己跑一遍
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;三层结构和每个概念的职责：&lt;a href="https://docs.thandoff.com/guide/" rel="noopener noreferrer"&gt;https://docs.thandoff.com/guide/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;部署到自己的机器（Docker Compose 或 Debian/Ubuntu systemd 一键安装）：&lt;a href="https://docs.thandoff.com/guide/install" rel="noopener noreferrer"&gt;https://docs.thandoff.com/guide/install&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;接入远程节点：&lt;a href="https://docs.thandoff.com/integrations/remote-nodes" rel="noopener noreferrer"&gt;https://docs.thandoff.com/integrations/remote-nodes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;「关闭网页会不会停任务」「停 Control Plane 会不会停实例」：&lt;a href="https://docs.thandoff.com/faq" rel="noopener noreferrer"&gt;https://docs.thandoff.com/faq&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>selfhosted</category>
      <category>devtools</category>
      <category>architecture</category>
    </item>
    <item>
      <title>A review checklist for AI coding agent output (7 things I check before trusting a change)</title>
      <dc:creator>EdgeStorage</dc:creator>
      <pubDate>Tue, 15 Sep 2026 02:08:05 +0000</pubDate>
      <link>https://dev.to/edgestorage/a-review-checklist-for-ai-coding-agent-output-7-things-i-check-before-trusting-a-change-276c</link>
      <guid>https://dev.to/edgestorage/a-review-checklist-for-ai-coding-agent-output-7-things-i-check-before-trusting-a-change-276c</guid>
      <description>&lt;p&gt;Every team running an AI coding agent eventually hits the same wall: the agent produces a lot of output, and reviewing that output is now the bottleneck. A terminal transcript is not a review artifact. Here is the checklist I use before I trust an agent's change, and the tooling each line implies.&lt;/p&gt;

&lt;h2&gt;
  
  
  The checklist
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Can I see the change as a diff, not as prose?&lt;/strong&gt;&lt;br&gt;
If the only thing you get is a summary, you are reviewing a claim, not code. Ask for the diff first. Everything after this line only matters if this one is true.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Do I know which directory the agent was in?&lt;/strong&gt;&lt;br&gt;
Ambiguous working directories are the root cause of a surprising number of "it edited the wrong file" incidents. Pin the path explicitly and make it part of the job record.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Is the change isolated to a ref I can check out?&lt;/strong&gt;&lt;br&gt;
A branch or a worktree means you can test the change yourself, and revert is one command. If the agent mutated your working tree in place, you have already lost the ability to compare. There is a full walkthrough of the worktree approach in the &lt;a href="https://docs.thandoff.com/tutorials/worktree-session" rel="noopener noreferrer"&gt;worktree session tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Which commands did it run, and did any of them touch the network?&lt;/strong&gt;&lt;br&gt;
Install scripts, dependency bumps and &lt;code&gt;curl | sh&lt;/code&gt; are where the sharp edges live. Log tool activity, not just the final text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Where did the credentials come from?&lt;/strong&gt;&lt;br&gt;
If the agent needed a private remote, the token should be scoped to that host and short-lived where possible. Broad credentials handed to an autonomous process is how a small mistake becomes an incident — see &lt;a href="https://docs.thandoff.com/security/git-credentials" rel="noopener noreferrer"&gt;git credentials&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Can a second person reproduce the run?&lt;/strong&gt;&lt;br&gt;
Same image, same runtime, same starting commit. If reproducing it takes a verbal explanation, it is not a process yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. What stopped it?&lt;/strong&gt;&lt;br&gt;
Time limit, token limit, an approval gate, or completion — you should be able to tell from the session state alone. Getting the states named unambiguously is worth doing early; a shared &lt;a href="https://docs.thandoff.com/reference/status-glossary" rel="noopener noreferrer"&gt;status glossary&lt;/a&gt; removes a lot of ambiguity from handoffs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this implies about your setup
&lt;/h2&gt;

&lt;p&gt;Once you write the checklist down, the requirements fall out almost mechanically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sessions need an explicit working directory and state,&lt;/li&gt;
&lt;li&gt;workspaces need to be disposable and isolated, so a bad run cannot poison the next one (&lt;a href="https://docs.thandoff.com/instances/" rel="noopener noreferrer"&gt;instances&lt;/a&gt;),&lt;/li&gt;
&lt;li&gt;Git review needs to be native, not bolted on (&lt;a href="https://docs.thandoff.com/workspace/repository" rel="noopener noreferrer"&gt;repository workspace&lt;/a&gt;),&lt;/li&gt;
&lt;li&gt;automations that repeat a routine need their own audit trail (&lt;a href="https://docs.thandoff.com/automation/" rel="noopener noreferrer"&gt;automation&lt;/a&gt;, &lt;a href="https://docs.thandoff.com/automation/actions" rel="noopener noreferrer"&gt;actions&lt;/a&gt;),&lt;/li&gt;
&lt;li&gt;and the whole thing has to run where the code runs, or the credential question answers itself badly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The uncomfortable part
&lt;/h2&gt;

&lt;p&gt;Most of this checklist exists because I did the opposite first. I let an agent work directly in a checkout I cared about, reviewed the summary, and merged. It worked — for about three weeks. The failure was not a dramatic bug; it was a dependency change nobody noticed until a deploy.&lt;/p&gt;

&lt;p&gt;Since then I run every agent task in a disposable workspace, with the diff as the deliverable. That workflow is what I packaged as &lt;a href="https://github.com/edgestorage/task-handoff" rel="noopener noreferrer"&gt;TaskHandoff&lt;/a&gt;: a self-hosted control plane where each task gets its own Local or Docker workspace, sessions are reviewable, and Git changes arrive as a branch or worktree you can test. Apache-2.0, and the docs start at &lt;a href="https://docs.thandoff.com/" rel="noopener noreferrer"&gt;docs.thandoff.com&lt;/a&gt; (&lt;a href="https://docs.thandoff.com/guide/install" rel="noopener noreferrer"&gt;installation&lt;/a&gt;, &lt;a href="https://docs.thandoff.com/faq" rel="noopener noreferrer"&gt;FAQ&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;What is on your checklist? I am particularly curious whether people gate on cost, on network access, or on the diff size.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>codequality</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Self-hosting an AI coding agent workbench: running Codex on my own machines</title>
      <dc:creator>EdgeStorage</dc:creator>
      <pubDate>Mon, 14 Sep 2026 17:34:57 +0000</pubDate>
      <link>https://dev.to/edgestorage/self-hosting-an-ai-coding-agent-workbench-running-codex-on-my-own-machines-58fc</link>
      <guid>https://dev.to/edgestorage/self-hosting-an-ai-coding-agent-workbench-running-codex-on-my-own-machines-58fc</guid>
      <description>&lt;p&gt;Most AI coding assistants assume you are fine sending your repository somewhere else. For side projects that is a reasonable trade. For anything with a client contract, an internal service, or a repository you cannot move, it is not.&lt;/p&gt;

&lt;p&gt;The interesting shift of the last year is that the &lt;em&gt;agent&lt;/em&gt; part has become a commodity — Codex, OpenCode, Claude Code and friends all expose a CLI you can run yourself. What is still missing for most teams is everything &lt;em&gt;around&lt;/em&gt; the agent: where does it run, which filesystem does it see, who reviews the diff, and how do you run the same routine twice without re-explaining it from scratch.&lt;/p&gt;

&lt;p&gt;I have been building that layer for a while. Here is the model that ended up working for me, and the concrete pieces you need if you want to reproduce it.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Separate the control plane from the machine that runs the agent
&lt;/h2&gt;

&lt;p&gt;The mistake I made first was treating "the agent" as the single thing to manage. In practice there are three distinct things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;control plane&lt;/strong&gt; that owns identity, sessions and routing,&lt;/li&gt;
&lt;li&gt;one or more &lt;strong&gt;nodes&lt;/strong&gt; — your laptop, a home server, a cheap VPS — that can actually execute work,&lt;/li&gt;
&lt;li&gt;a set of &lt;strong&gt;workspaces&lt;/strong&gt; that give each task an isolated filesystem and process tree.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once those are three separate concepts, everything else gets easier. A session is not "a chat window", it is a job with an owner, a working directory, a runtime and a status. You can list it, resume it, hand it to a colleague, or kill it.&lt;/p&gt;

&lt;p&gt;If you want the shape of that in practice, the &lt;a href="https://docs.thandoff.com/guide/first-setup" rel="noopener noreferrer"&gt;first setup guide&lt;/a&gt; walks the control-plane/node split end to end, and &lt;a href="https://docs.thandoff.com/integrations/remote-nodes" rel="noopener noreferrer"&gt;remote nodes&lt;/a&gt; covers what it takes to attach a second machine — the interesting part is that node enrollment is a token, not a VPN, so a box behind NAT is reachable without exposing your whole network.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. One workspace per task, not per conversation
&lt;/h2&gt;

&lt;p&gt;The second change was moving from "one container per project" to "one workspace per task". A task workspace is disposable: it gets a working directory, a defined runtime, and a bounded life. Two agents working on the same repository in parallel stop stepping on each other's files, and cleaning up is a delete rather than an archaeology exercise.&lt;/p&gt;

&lt;p&gt;That works with either a local runtime (run directly on the host, reuse the tools you already have installed) or a Docker runtime (each workspace in its own container). I default to Docker for anything that installs dependencies, and local for repositories where I want the host toolchain.&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://docs.thandoff.com/instances/" rel="noopener noreferrer"&gt;instances&lt;/a&gt; for how workspaces are created and restored, and &lt;a href="https://docs.thandoff.com/workspace/repository" rel="noopener noreferrer"&gt;workspace: repository&lt;/a&gt; if you want the Git side of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Make the agent's output reviewable, not just readable
&lt;/h2&gt;

&lt;p&gt;A terminal scrollback is a terrible review artifact. What I want after an agent finishes is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the diff, in a form I can read like a pull request,&lt;/li&gt;
&lt;li&gt;the commands it actually ran,&lt;/li&gt;
&lt;li&gt;a branch or worktree I can check out and test myself.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The practical trick is to have the agent deliver onto a separate ref instead of mutating your working tree. Worktrees are the cheapest version of that: the agent works on its own checkout, &lt;code&gt;git diff&lt;/code&gt; shows you exactly what happened, and merging is a normal Git operation. There is a walkthrough of that workflow in the &lt;a href="https://docs.thandoff.com/tutorials/worktree-session" rel="noopener noreferrer"&gt;worktree session tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If the agent needs to pull a private dependency, the credentials question comes up immediately. Scope the token to the specific remote, use it for provisioning, and keep it out of the agent's environment where possible — &lt;a href="https://docs.thandoff.com/security/git-credentials" rel="noopener noreferrer"&gt;git credentials&lt;/a&gt; goes into that in more detail than is comfortable.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Automate the routines you repeat
&lt;/h2&gt;

&lt;p&gt;After a few weeks you notice you are running the same sequence over and over: pull, update a lockfile, run the test suite, fix the lint, open a branch. That is where a small automation layer pays for itself. I ended up with four primitives that compose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;documents&lt;/strong&gt; — the instructions, versioned,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;triggers&lt;/strong&gt; — what starts it (schedule, webhook, manual),&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;actions&lt;/strong&gt; — what it does (run a session, run a command, notify),&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;schedules&lt;/strong&gt; — when.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once those exist you stop pasting the same prompt and start reviewing output instead. The &lt;a href="https://docs.thandoff.com/automation/" rel="noopener noreferrer"&gt;automation docs&lt;/a&gt; cover the model, and &lt;a href="https://docs.thandoff.com/automation/actions" rel="noopener noreferrer"&gt;actions&lt;/a&gt; is the part with the sharp edges.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would tell someone starting today
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Run the agent where the code lives. Latency is the least of your problems; data residency and filesystem access are the real ones.&lt;/li&gt;
&lt;li&gt;Give every task a disposable workspace. It removes an entire class of "which file did it overwrite" bugs.&lt;/li&gt;
&lt;li&gt;Make the agent deliver a diff, not a summary. Reviewing summaries is how you end up trusting a black box.&lt;/li&gt;
&lt;li&gt;Automate only after you have run the routine manually three times.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to try this shape without assembling it yourself, this is exactly what I have been packaging as &lt;a href="https://github.com/edgestorage/task-handoff" rel="noopener noreferrer"&gt;TaskHandoff&lt;/a&gt; — a self-hosted control plane for AI coding agents, Apache-2.0, with local and remote nodes, per-task Local or Docker workspaces, Codex and OpenCode sessions, Git review, and scheduled automation. Start with &lt;a href="https://docs.thandoff.com/guide/install" rel="noopener noreferrer"&gt;installation&lt;/a&gt;, or skim the &lt;a href="https://docs.thandoff.com/faq" rel="noopener noreferrer"&gt;FAQ&lt;/a&gt; if you are wondering whether it needs Docker or a public IP.&lt;/p&gt;

&lt;p&gt;The docs are at &lt;strong&gt;&lt;a href="https://docs.thandoff.com/" rel="noopener noreferrer"&gt;docs.thandoff.com&lt;/a&gt;&lt;/strong&gt; (also in &lt;a href="https://docs.thandoff.com/" rel="noopener noreferrer"&gt;Chinese&lt;/a&gt; — the site is bilingual, add &lt;code&gt;/en/&lt;/code&gt; for the English tree).&lt;/p&gt;

&lt;p&gt;What is your setup? Are you running agents on your laptop, a home lab box, or somewhere properly boring like a VPS? I am curious which of the three layers above people are missing.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>selfhosted</category>
      <category>devtools</category>
    </item>
  </channel>
</rss>
