<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Scheduler | Francis Du</title>
    <link>https://francisdu.com/tags/scheduler/</link>
      <atom:link href="https://francisdu.com/tags/scheduler/index.xml" rel="self" type="application/rss+xml" />
    <description>💻Data Engineer | 🦀 Rustacean | 📷 Photographer | 🤖Vibe Coder</description>
    <generator>Hugo 0.166.0</generator><language>zh-CN</language><copyright>© Francis Du</copyright><lastBuildDate>Sat, 12 Sep 2026 05:31:00 +0800</lastBuildDate>
    <item>
      <title>wcode v0.6.1：`parallel_tools` 不再按层等</title>
      <link>https://francisdu.com/blog/wcode-v0-6-1/</link>
      <pubDate>Sat, 12 Sep 2026 05:31:00 +0800</pubDate>
      <guid>https://francisdu.com/blog/wcode-v0-6-1/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://francisdu.com/blog/wcode-v0-6/&#34;&gt;v0.6&lt;/a&gt; 开始在 &lt;code&gt;agent_context&lt;/code&gt; 里标出可以并行的工作。接着我去看 Runtime 本身，发现 &lt;code&gt;parallel_tools&lt;/code&gt; 虽然已经有依赖图，执行时还保留着按层推进的做法。&lt;/p&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;A ─→ C
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;B ─────────→ D
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;假设 A 100ms 完成，B 要 5s，C 只依赖 A。&lt;/p&gt;
&lt;p&gt;按“层”执行时，很容易变成：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;第一层：A + B
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ↓ 等两者都结束
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;第二层：C + D
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;C 明明在 100ms 时已经 Ready，却因为一个和自己无关的 B，被迫多等几秒。&lt;/p&gt;
&lt;p&gt;所以 v0.6.1 我把调度器改成了 completion-driven。&lt;/p&gt;
&lt;figure class=&#34;content-image&#34;&gt;&lt;img src=&#34;https://francisdu.com/img/wcode/wcode-tui_hu_13e59e78ee0ce797.webp&#34; alt=&#34;wcode TUI&#34;loading=&#34;lazy&#34; decoding=&#34;async&#34; srcset=&#34;https://francisdu.com/img/wcode/wcode-tui_hu_13e59e78ee0ce797.webp 960w, https://francisdu.com/img/wcode/wcode-tui_hu_6437a17a51f06ca3.webp 1800w&#34; sizes=&#34;(max-width: 900px) calc(100vw - 32px), 900px&#34; width=&#34;3420&#34; height=&#34;2146&#34;&gt;&lt;/figure&gt;&lt;h2 id=&#34;ready-的任务不应该等同层慢分支&#34;&gt;Ready 的任务不应该等同层慢分支&lt;a class=&#34;heading-anchor&#34; href=&#34;#ready-%e7%9a%84%e4%bb%bb%e5%8a%a1%e4%b8%8d%e5%ba%94%e8%af%a5%e7%ad%89%e5%90%8c%e5%b1%82%e6%85%a2%e5%88%86%e6%94%af&#34; aria-label=&#34;章节链接：Ready 的任务不应该等同层慢分支&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;现在 Scheduler 只看一个问题：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;这个任务自己的前置依赖是不是已经完成？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;满足就进入 Ready Queue。&lt;/p&gt;
&lt;p&gt;实际任务图通常不整齐。一个慢测试、一个大文件读取、一个外部命令，不该顺手把无关分支也拖住。&lt;/p&gt;
&lt;h2 id=&#34;失败也只应该污染依赖它的分支&#34;&gt;失败也只应该污染依赖它的分支&lt;a class=&#34;heading-anchor&#34; href=&#34;#%e5%a4%b1%e8%b4%a5%e4%b9%9f%e5%8f%aa%e5%ba%94%e8%af%a5%e6%b1%a1%e6%9f%93%e4%be%9d%e8%b5%96%e5%ae%83%e7%9a%84%e5%88%86%e6%94%af&#34; aria-label=&#34;章节链接：失败也只应该污染依赖它的分支&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;同样的原则也用在失败上。&lt;/p&gt;
&lt;p&gt;如果：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;A → C
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;B → D
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A 失败，那么 C 应该被 Skip，因为它的前置不成立；但 B 和 D 不应该跟着一起失败。&lt;/p&gt;
&lt;p&gt;所以运行时会区分依赖失败导致的 Skip、当前任务自己的运行失败，以及完全独立、仍然可以继续的分支。&lt;/p&gt;
&lt;p&gt;这对批量读取、搜索、验证尤其重要。Agent 不应该因为一个不存在的可选文件，就失去其他已经能确定的结果。&lt;/p&gt;
&lt;h2 id=&#34;partial-不能假装-complete&#34;&gt;Partial 不能假装 Complete&lt;a class=&#34;heading-anchor&#34; href=&#34;#partial-%e4%b8%8d%e8%83%bd%e5%81%87%e8%a3%85-complete&#34; aria-label=&#34;章节链接：Partial 不能假装 Complete&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这轮我把批处理结果的语义也重新收紧了。&lt;/p&gt;
&lt;p&gt;批量读、创建、编辑、移动会明确报告：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;complete
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;partial
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;failed
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;并保留逐项结果。&lt;/p&gt;
&lt;p&gt;如果一个批次只完成了一部分，MCP Result 会保守地带错误标记，依赖整个批次的后续任务不能把它当作完整输入。独立分支仍然继续。&lt;/p&gt;
&lt;p&gt;重试也不应该重放整个批次。先核对状态，只重试失败项。这对写操作尤其重要，因为“请求失败”不等于“所有副作用都没发生”。&lt;/p&gt;
&lt;h2 id=&#34;并行写入最怕错误合并&#34;&gt;并行写入最怕错误合并&lt;a class=&#34;heading-anchor&#34; href=&#34;#%e5%b9%b6%e8%a1%8c%e5%86%99%e5%85%a5%e6%9c%80%e6%80%95%e9%94%99%e8%af%af%e5%90%88%e5%b9%b6&#34; aria-label=&#34;章节链接：并行写入最怕错误合并&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;为了减少 Tool Call，wcode 会在安全条件满足时合并部分文件编辑。&lt;/p&gt;
&lt;p&gt;这里我宁愿少合并一点。&lt;/p&gt;
&lt;p&gt;同一个文件、同一个 SHA 的 Edit，如果中间隔着一个依赖 Read、Move 或其他会改变语义的操作，就不能跨过去合并。否则 Scheduler 会把原本有先后关系的工作偷偷变成一个事务。&lt;/p&gt;
&lt;p&gt;合并前也会检查既有的 128 项编辑上限，不能因为优化路径就绕过原来的安全边界。&lt;/p&gt;
&lt;p&gt;如果一次优化改掉了原本的先后关系，那就已经不是调度优化了。&lt;/p&gt;
&lt;h2 id=&#34;cancel-也必须真的往下传&#34;&gt;Cancel 也必须真的往下传&lt;a class=&#34;heading-anchor&#34; href=&#34;#cancel-%e4%b9%9f%e5%bf%85%e9%a1%bb%e7%9c%9f%e7%9a%84%e5%be%80%e4%b8%8b%e4%bc%a0&#34; aria-label=&#34;章节链接：Cancel 也必须真的往下传&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;异步系统里还有一个很烦的问题：外层任务取消了，不代表里面已经排队的工作自动消失。&lt;/p&gt;
&lt;p&gt;如果父请求断开，而子任务还在队列里，最糟的情况是用户以为“已经取消”，几秒后某个写操作才突然开始。&lt;/p&gt;
&lt;p&gt;v0.6.1 把取消传播继续往下压：父任务取消会取消仍在排队的子任务；持久 MCP Task 被取消或 TTL 过期，也会取消它拥有的执行任务。&lt;/p&gt;
&lt;p&gt;已经开始的阻塞文件操作不会假装回滚。这条边界也要说清楚。&lt;/p&gt;
&lt;h2 id=&#34;并发许可要跟真实工作走&#34;&gt;并发许可要跟真实工作走&lt;a class=&#34;heading-anchor&#34; href=&#34;#%e5%b9%b6%e5%8f%91%e8%ae%b8%e5%8f%af%e8%a6%81%e8%b7%9f%e7%9c%9f%e5%ae%9e%e5%b7%a5%e4%bd%9c%e8%b5%b0&#34; aria-label=&#34;章节链接：并发许可要跟真实工作走&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;以前还有一种容易让面板数据看起来比实际更漂亮的情况：外层 Future 被取消以后，如果并发许可提前释放，但底层阻塞工作其实还在跑，就会出现“系统看起来有空位，实际上旧工作没结束”。&lt;/p&gt;
&lt;p&gt;现在已经启动的阻塞工作会继续持有对应许可，直到结束或异常退出。排队工作取消则会释放预留。&lt;/p&gt;
&lt;p&gt;我更愿意让数字难看一点，也不想让 Scheduler 对自己的真实资源状态撒谎。&lt;/p&gt;
&lt;h2 id=&#34;context-也继续往目标优先收紧&#34;&gt;Context 也继续往目标优先收紧&lt;a class=&#34;heading-anchor&#34; href=&#34;#context-%e4%b9%9f%e7%bb%a7%e7%bb%ad%e5%be%80%e7%9b%ae%e6%a0%87%e4%bc%98%e5%85%88%e6%94%b6%e7%b4%a7&#34; aria-label=&#34;章节链接：Context 也继续往目标优先收紧&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;顺手还改了一轮 &lt;code&gt;agent_context&lt;/code&gt; 的候选排序：精确定义优先于相似辅助函数；直接目标文件优先于按字母排在前面的 Design 映射；并行建议开始结合当前 Scope、活动 Worklist Lane 和运行时并发上限，而不是固定最多四路。&lt;/p&gt;
&lt;p&gt;Lane 数只用来给 Agent 提示，并不能证明两件事真的独立。最后仍然由真实资源和依赖决定能不能一起跑。&lt;/p&gt;
&lt;h2 id=&#34;observatory-不再把有映射当已验证&#34;&gt;Observatory 不再把“有映射”当“已验证”&lt;a class=&#34;heading-anchor&#34; href=&#34;#observatory-%e4%b8%8d%e5%86%8d%e6%8a%8a%e6%9c%89%e6%98%a0%e5%b0%84%e5%bd%93%e5%b7%b2%e9%aa%8c%e8%af%81&#34; aria-label=&#34;章节链接：Observatory 不再把“有映射”当“已验证”&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Project Observatory 这轮还修了一个我很在意的表达问题。&lt;/p&gt;
&lt;p&gt;以前“某个 Requirement 有 Verification 映射”和“这个 Revision 上真的执行并通过了验证”在视觉上太容易靠近。&lt;/p&gt;
&lt;p&gt;现在会明确区分：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;verification mapped
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;executed
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;passed
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fresh proof
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;因为有测试文件，不等于测试跑过；测试跑过，也不等于它属于现在这份代码。&lt;/p&gt;
&lt;h2 id=&#34;这版主要把无意义等待拿掉&#34;&gt;这版主要把无意义等待拿掉&lt;a class=&#34;heading-anchor&#34; href=&#34;#%e8%bf%99%e7%89%88%e4%b8%bb%e8%a6%81%e6%8a%8a%e6%97%a0%e6%84%8f%e4%b9%89%e7%ad%89%e5%be%85%e6%8b%bf%e6%8e%89&#34; aria-label=&#34;章节链接：这版主要把无意义等待拿掉&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;v0.6.1 基本都在修执行语义：Ready 就进队列，失败只截断依赖分支，Partial 明确报告 Partial，Cancel 继续往子任务传，许可跟着真实工作释放。&lt;/p&gt;
&lt;p&gt;它不会把本来串行的任务凭空变快，但长任务里少掉几次无关等待，体感很明显。&lt;/p&gt;
&lt;p&gt;再往后到 v0.6.2，我又继续拆了一个误区：&lt;strong&gt;外层 &lt;code&gt;SLOTS&lt;/code&gt; 很高，不代表真实吞吐就高。&lt;/strong&gt;&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>