Skip to content

fix(dag): harden replan verdict gate — string-output bypass + transient pause strand (audit follow-up) #330

Description

@LeXwDeX

来源

对近期 DAG 变更(#325/#326/#327 及与 #317/#318 的交互)做审计后的两条可执行发现(均 SOFT,无 HARD):

SOFT-2:裁决门可被字符串输出绕过

loop.ts 的门判定只对 Schema.decodeUnknownOption(GateReplanVerdict)(node.output) 解码——当报告型检查点没有 output_schema、或子代理以纯文本形式回复 {"verdict":"replan"} 时,node.output 是字符串,Struct 解码返回 None,门不触发——复现 #322 的整图空转。可达路径:replan 片段新增的检查点不走 authoring 检查(ADR-0003 已记录的豁免),因此可以不带 output_schema。

SOFT-3:pause 失败可致 workflow 静默搁浅

门触发时若 dag.pause 因瞬态原因失败(现实案例:并发长 replan 持有 workflow 锁导致 WORKFLOW_LOCK_TIMEOUT),原实现仅记日志 + setPaused(false) + 跳过 spawnReady——既不重试也不唤醒父会话(readWakeBatch 在 getReadyNodes 非空时不视为投递边界),workflow 会卡死到无关事件到来。

修法

  1. 字符串输出先经 Schema.UnknownFromJsonString 解析再匹配 verdict(与 dag.ts 既有 parseJsonOption 同型)。
  2. 门 pause 先重试一次,仍失败则回读持久状态兜底,确保永不静默搁浅。

验收

  • 新增回归:report_to_parent 无 output_schema、字符串 verdict {"verdict":"replan"} → workflow paused、下游保持 pending。
  • 既有 gate 回归(object verdict pause/continue/resume)全绿。
  • test/dag/ 561 pass / 0 fail;packages/opencode typecheck 干净。

未处置(记录在案,评估后接受或降级)

  • SOFT-1:gate-pause 会把 STEPPING 工作流在 replan-resume 后降级为 RUNNING(step 语义丢失)——罕见交互,另行评估。
  • SOFT-4:WorkflowReplanned 处理器 spawn 前不刷新 paused 标志(既有竞态,显式 pause-first 场景)——另行评估。
  • SOFT-5:authoring condition 检查对 OR-first/括号条件只取最左 LHS,可能误报——fail-closed 方向可辩护,保留。
  • SOFT-6:gate-paused 全沉降时 checkCompletion 的 complete/fail 转移违规仅产生日志噪音——良性,保留。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions