github esengine/DeepSeek-Reasonix studio-v2.8.0
Reasonix Studio v2.8.0

pre-release2 hours ago

这一版有两条线。一条是调度开始说真话:排队的节点会说是谁在挡它,争用的名额给挡住最多工作的那个,扇出被计价而不只是计数。另一条是把老桌面退役时留下的孤儿逐个找回来——托盘、系统通知、崩溃日志、last-known-good 快照,它们的调用者跟着那个窗口一起没了,而没有任何一道门会说。这一版也补上了会说的那几道门。自 2.7.0 起 27 个提交。

新增

一个排队的节点会说是哪条约束在挡它

  • 「排队中」此前是三种约束共用的一个词,而它们只有两种解法:两个是会话设置(子代理总数上限,以及并发写者上限——后者会在总容量还空着的时候就卡住一个写者),第三个是别人已经持有的写路径,没有任何设置能放开它,只有互不相交的路径或者一条给这两者排序的边。调度器是唯一分得清的地方,而它此前的回答是一句为某一个调用方的错误消息写的话,下游拿不到。
  • WaitCause 进入图的词汇表,和 NodeStateGrant 并列。原因在运行开始后仍然保留:状态说「现在在跑」,它说「之前等的是什么」。
  • 扇出的计价跟着分开。此前一个数字把 ready 到 running 之间所有的等待加在一起,名字却说这是并发上限造成的——于是一个卡在写声明上的成员,被引述成「把上限调高」的理由,而调高根本不会让它动。现在是两个数字,因为答案本来就有两个。

争用的名额给挡住最多工作的那个

  • fleet 的计划早就证明了依赖 DAG 并发布了它,而真正决定谁跑的调度器从来没看到过:一个 AcquireRequest 只带写声明和标签,不带它在图里的位置。于是名额按先来后到发放——最长链的头部排在什么都解锁不了的叶子后面,这个等待一比一地落在整次运行的完工时间上。
  • 计划现在测量每个条目的秩(它下方最长的依赖链),并带到 acquire;泵按权重从重到轻放行,同权重内保持到达顺序。会话名额满了之后的每一次授予都被覆盖到——扇出宽于上限时,那几乎就是全部。
  • 第一次填充没有被覆盖,把启动循环排序也做不到:一批 goroutine 里谁先到达调度器是 Go 运行时的决定。实测在上限为 2 时,按最重优先启动只拿到理想首填权重的 41.8%,声明顺序反而是 51.9%,上限为 1 和 3 时打平——从不更好,有时更差,因为运行时偏向最后创建的那个 goroutine。循环原样保留,并写明了原因。
  • 一次运行现在先排队再构建。此前它会把整个子代理造完——注册表、运行时、以及持有存储租约的那份轨迹——然后才去排队,于是宽于会话上限的扇出会为每个还没开始的条目占着一份,而那段构建的每一毫秒都被记在一个并非肇因的并发上限账上。

扇出被计价,而不只是计数

  • 图里早就带着基准需要的每一个时间戳,却没有任何东西折叠它们,于是委派只被报成「有几个子代理」,从来没被报成「这个形状买到了什么」。工作量对墙钟是加速比;关键路径是声明的依赖强加的地板,超出它的墙钟是调度损失;名额等待是并发上限向那些已经无所可等的成员收取的费用——那是图上任何一条边都画不出来的等待。
  • 效果测试现在钉住时间戳本身。没有它们,上面每一个数字都会折叠成零,而那读起来像是「这次扇出没花时间」,不像「这次测量根本没发生」。

parallel_tasks 的节点会说自己以什么身份在跑

  • parallel_tasks 每个任务各带模型与 effort,所以同一组里的两个成员未必是同一种工人。图此前把它们画得一模一样:fleet 节点带着 profile、模型、effort 和授权,而这些只带一个标签。一个想知道「为什么扇出的这条臂比那条贵」的读者,在图上找不到任何可以回答的东西——工具提供了这个区别,图把它抹掉了。

记忆的修订历史可以打开了

  • 记忆面板一直告诉读者,保存「会记录一条新修订,旧的还在」。而界面里没有任何东西能展示这段历史或者退回去:GET /memory/revisionsPOST /memory/restore 两个端点都在服务,两个都有 Controller 端口方法,前端一个都没叫过——承诺在那里,且无法验证。
  • 一条已有第二次修订的条目会出现历史入口,列出当前之前的每一条修订及其日期和正文,逐条可恢复。恢复是追加一条修订而不是回退到某一条,面板会这么说明:一次会丢掉后续修订的恢复,销毁的恰好是读者打开历史想要对照的那个版本。

三道会说话的门

  • 导出的内核 API 没有调用者就失败。last-known-good 快照之所以死掉,是因为删掉一个前端之后,它的被调用方连同实现、测试和文档注释都完好地留了下来。CI 里没有任何东西读「它有没有调用者」,于是这棵树绿了八天,而一条恢复路径读的是一个不再有人写的文件。这条规则跨整棵树统计调用者——desktop/ 是独立模块,也是 internal/ 的真实消费者,只扫根模块会把它的 API 判成死的。测试和文档注释都不算调用者,因为它们正是让孤儿保持温热和看起来接线正常的东西。现有 328 个孤儿记为棘轮基线。
  • 声明为敏感的路径守住自己的覆盖率。REASONIX.md 把这些路径称作「改错了不是功能有 bug,而是边界上有个洞」。量出来才发现它们是内核里测得最差的部分而不是最好的:shellsafe 59.3%、shellparse 63.6%、sandbox 69.4%、netclient 47.2%,而全树中位数是 76.5%。门读的就是运行时读的那份 sensitive: 清单,声明一次拿到两个效果,没有第二份清单会走样。地板按声明路径逐个测量,且按文件而非按包——internal/control/approval.go 是敏感的而它所在的包不是,单独测它是 93.9%,任何包级数字都显示不出来。
  • 桌面端那份线协议类型的副本被对齐到内核agentgraph 存在的理由就是每个消费者折叠同样的增量,于是无头运行报出的图就是桌面画出的图。而桌面那份折叠是手写的第二份副本——类型和合并都是——没有任何东西检查两边带的字段一致。这一周往内核加一个字段,意味着要记得在另外两个地方手工加上;下一个人不会记得,而两套工具链都不会说,因为它们分开编译、只在运行时通过 JSON 相遇。

并发这项声明现在有一条量得动它的基准臂

  • 委派做了三项声明,而 upstream-edge 只能结算其中一项:它的两个任务都是严格链条,加速比按构造就是 1.00x,里面观察不到任何调度决策。fanout-width 固定了一个三项研究互不相干的形状,于是会话并发上限成了唯一决定它们是否重叠的东西。

修复

  • 切过模型之后,托盘、系统通知和用量计数就永久失聪#9448)。宿主给每个面板的事件流装饰一次——状态图标的折叠、桌面通知、用量计数器——然后用它构建控制器。而这层装饰只活在构建的调用点上,于是每一条重建控制器的路径都把替代品接到了裸广播器上:切模型、切 effort、重载扩展、换工作区,以及保存供应商凭据——那正是新装机做的第一件事。而且不会自愈:有活跃工作时重建会被拒绝,所以那些观察者看到的最后一个事件必然是上一轮的 TurnDone,此后它们就停在那里过完这个面板的余生。托盘曾经在一次十四分钟的回合里一直显示「没有在跑的」。现在由重建者持有它自己发射进去的那个 sink,而不是由碰巧最先构建的那个调用点。
  • 两个面板同时变化时,图标会停在一个已经过去的状态上#9448)。折叠在锁内取、上报在锁外做,于是两份上报可能反序到达图标;输掉的那次会把图标留在追踪器早已越过的状态上,而且没有东西纠正它——折叠只上报变化,所以一次画错会一直挂到下一次真正的变化。上报现在串行化,并且描述的是它落笔那一刻的折叠。
  • Windows 任务栏标识没有被设置。Windows 按进程的 AppUserModelID 给任务栏按钮分组、给 toast 路由,而开始菜单快捷方式必须带同一个,否则通知会被丢弃。设置这两者的调用住在已退役的那个外壳里;它走之后这两个函数失去了调用者,随后被「删掉无引用导出」的清扫带走——留下 internal/notify 用一个没人认领的身份发 toast。恢复,并从窗口自己的启动路径调用,在第一个窗口存在之前,那正是 Windows 读它的时刻。
  • 窗口没能活下来的崩溃留不下任何可读的东西#9408)。Studio 根本没有调用过 crashreport,只有 CLI 调用了,所以连一个 Go panic 都没被记录下来。而那还是小的一半:迄今为止上报的每一次 Studio 崩溃都是 GTK 或 WebKit 内部的致命信号,任何 recover 都够不着——运行时把那份 trace 直接写进文件描述符,所以在 os.Stderr 上做 tee 会留下每一行日志,唯独丢掉唯一值钱的那行。于是改成重定向而不是包装:窗口在 <home>/logs 下开一份日志并 dup 到描述符上,原来有终端时先留一份副本,所以从 shell 里跑 Studio 仍然会打印。
  • last-known-good 快照没有写入者。恢复读的是一份自 Wails 外壳被删之后就没人再写过的快照。那个外壳持有仅有的两处 RecordHealthyConfig 调用,于是从 908bc3e 起,两条读路径一直在把「当天恰好是什么配置」当成已知良好还原回去。在这台机器上那个文件陈旧了 16 天、161 行差异,而 doctor repair --apply 会把它盖回活配置上且不作声。外壳用「窗口可见了」证明健康,那是只有 Wails 才有的信号;一次成功的装配对每个前端都证明同一件事,于是这条记录改骑在 BuildRuntime 上。两条恢复路径现在都会说明自己打算信任的东西有多旧。
  • 重开窗口后运行图丢掉了依赖。面板本来就会把轨迹重放进图的 reducer,但线协议日志在落盘途中把每一条 graph_delta 都丢了,于是重开的窗口画出的是一次什么都没等过的运行。依赖和被采纳的答案不存在于流里的任何其它地方——和工具标记不同,它们无法被重新推导。
  • 委派回执被那个同时看两种事件的包装器吞掉inboxEventSink 手工转发七种可选 sink 能力,漏了三种,于是每个子运行的回执都死在负责记账的那个 sink 下面一层。一次基准因此把四个 fleet 成员报成「一个 agent 独自解决的任务」,整个委派小节渲染成空的,而它自己的测试全绿。
  • 停在提问上的一轮被当成还欠着工作addTurnExitTools 注册了两种按模型自己的条件结束一轮的方式:向用户提问,以及 conclude_blocked。而宿主自己那份「有哪些出路」的说明里只写了后者。于是一轮以「把问题交给用户」结束之后被直接推着继续,而那次运行——被用宿主的口吻告知恰好有三扇门、且没有一扇是「等」——自己回答了那个问题,还写进了记录里。
  • 两条续跑提示指向的不是同一批门。续跑一轮有两条宿主提示:readiness 续跑,和 Goal 状态机的「声称完成但仍有问题」。只有第一条被教过完成守卫放行哪些动作。Goal 那条仍然以「用 todo_write/complete_step 标记完成」结尾——那恰好是守卫拒绝的那个动作。
  • 字段类型错了,回答用的是宿主的词汇。参数契约此前只读一次调用提供了哪些字段,从不读里面放了什么。一个名字对、类型错的字段因此既不算未知也不算缺失:它通过了宿主做的每一项检查,答案来自 Go 的反序列化器——「cannot unmarshal string into Go struct field .evidence of type []agent.blockedEvidence」。那说的是宿主的类型而不是调用方的错误,而且要花掉一次发现它的往返。schema 本来就声明了每个属性的类型,参数本来就带着一个,比较它们和契约已经在名字上做的是同一种结构化读取。
  • 一次改动在被 review 覆盖之前一直留在 review 的范围内。review 门用一个索引回答两个问题——改动集是什么,以及对它的 review 要多新——于是后来一次无害的写入把窗口推过了它之前的一切。一次敏感编辑后面跟一次 README 写入,评分会掉到 Low 并丢掉整个结构化 review 要求。
  • 两个生成的 grader 在非 POSIX 上给出同样的判定。自语料落地以来,它们让 CI 的 windows-latest 作业每一次都挂,而且都不是 flake:一个用 signal.alarm 计时,而 Windows 的 Python 没有 SIGALRM;另一个把 cli.py 的标准输出和期望串直接比较,而 Windows 的 Python 在文本模式下写 CRLF。
  • gorilla/websocket 1.5.0 升到 1.5.3。govulncheck 此前报出七条能走到 1.5.0 那批公告的调用路径——飞书适配器的 socket,以及两个经由 io.ReadAll 读到压缩帧的读取器。现在它在这份代码调用得到的范围内找不到漏洞。

内部

  • Controller 按字段生命期拆开:78 个字段里,37 个在 New 里绑定一次且再不重新赋值,其余在会话运行期间会变。两类此前混在一张表里,于是 c.policyc.turn 在每一个调用点读起来都一样,而只有其中一个会在你眼皮底下变——那恰好是任何一个伸手去拿 c.mu 的人首先要回答的问题。
  • 清扫掉 57 个全树(含测试)无引用的导出函数,以及跟着死在它们后面的私有助手。
  • 覆盖率门在 Windows 上算出的包模式带着反斜杠:filepath.Dir 给出 internal\control,那不是 go test 能匹配的模式,而且因为反斜杠排在正斜杠之后,出来的顺序也跟着错——一个根因,同一条断言里两个症状。门本身跑在 ubuntu 上,所以它守的东西没受影响;它的测试跑在整个矩阵上。
  • 收集事件的测试 sink 在第二个发射者出现后仍然成立:inbox 增加变更监听器之后,存储会从自己的 goroutine 通知,六个测试开始在 -race 下失败——竞态从来不在控制器里,是这个测试装置没有办法把这件事说出来。

macOS 已用 Developer ID 签名并公证,打开时不需要清除隔离属性。Windows 包未签名 —— Authenticode 签名绑定在受保护的 main-v2 上,本分支拿不到它,所以 SmartScreen 会告警:选「更多信息」,再选「仍要运行」。Linux.deb 安装。

三个平台都能自更新:Linux 走自己的包,Windows 运行下一个安装器,macOS 替换自身 bundle。

Don't miss a new DeepSeek-Reasonix release

NewReleases is sending notifications on new releases.