mcpField
D
dagr-producer

dagr-producer

github.com/aemrebarutAgent 工作流134377 次安装9 周前更新
代码仓库

关于 dagr-producer

dagr-producer 是用于创建和维护 dagr 运行文件的 Agent 技能,文件由 dagr view 实时呈现。它要求先严格验证候选文件再原子发布,并明确规定任务状态、尝试记录、依赖关系和结果证据。

主要功能

  1. 01发布前严格验证
  2. 02原子更新运行文件
  3. 03建模任务、项目与尝试
  4. 04仅追加事件溯源记录
  5. 05为结果标注证据等级

适用场景

  1. 01创建新的 dagr 运行
  2. 02安全更新实时任务图
  3. 03记录重试与评审退回
  4. 04区分已验证与未验证完成

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/aemrebarut/herdr-dagr --skill dagr-producer

安装后在 Claude Code 中输入/dagr-producer确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

examples/
01-init.json
02-first-attempt.json
03-send-back.json
04-gate-promotion.json
05a-policy-declared.json
05b-policy-materialized.json
06-evidence-tiers.json
07-answer-question.json
08-project-scopes-and-message.json

详细介绍

dagr 生产者——写入运行记录,验证运行记录

你是 producer:运行文件的唯一写入者,dagr view 会实时渲染该文件。dagr 是一个表示内核:你负责声明任务事实,它只会根据这些事实推导出明确定义的视图信号。缺失或错误的事实仍会生成缺失或错误的图;没有工作流引擎会替你修正。

契约版本:"dagr": 3(仍可读取 v1/v2 文件;新运行请写入 v3)。

找到你的验证器(写入任何内容之前)

下面的检查循环是你能获得的唯一反馈,因此请先解析 dagr 二进制文件,并在无法找到时停止:

  1. 如果已设置,使用 $DAGR_BIN;
  2. 使用 command -v dagr(在 PATH 中查找);
  3. 使用插件/仓库构建产物:<dagr repo>/target/release/dagr——在 herdr 窗格中,运行时会注入 $HERDR_PLUGIN_ROOT,它就是 dagr 仓库根目录;
  4. 备用方案:cargo run --manifest-path <dagr repo>/Cargo.toml -- check …。

如果没有可用的验证器 → 不要开始编写运行文件;说明情况并 停止。未经验证的运行文件正是整个系统旨在防止的那种悄无声息的错误。

循环(不可妥协)

write run.json.tmp → dagr check run.json.tmp --strict --json → fix → repeat until []
                                                             → THEN rename over run.json

先验证候选文件,再发布——绝不能反过来。 窗格会立即渲染 run.json 中的内容;将无效候选文件重命名覆盖该文件,会在你迭代期间把错误状态展示给所有查看者。安全的事务流程:

  1. 将完整的新文档写入 run.json.tmp(位于同一目录——重命名必须是原子的,因此必须在同一文件系统中)。
  2. dagr check run.json.tmp --strict --json。退出码为 0 且输出为 [] 表示通过;退出码为 1 表示存在问题(E 代码表示错误;W 代码表示可表示但可疑——除非你能说明原因,否则应修复;--strict 会将其视为失败,建议使用)。退出码 2 表示验证器根本无法读取文件——这是路径或工具问题,而不是文档问题;这种情况下标准输出为空,因此绝不能将空输出视为通过。停止,重新执行预检,退出码非零时绝不发布。
  3. 只有通过后才执行:mv run.json.tmp run.json(原子重命名——窗格会在 mtime 变化时重新加载,绝不会看到写入一半或无效的文件)。
  4. 失败时:修复临时文件并重新检查。之前的实时文件保持不变——绝不要让 run.json 本身处于错误状态。

写入位置:窗格会依次查找 $DAGR_RUN、.dagr/run.json,然后是工作区 cwd 下的 run.json;如果这些文件都不存在,则会等待 .dagr/run.json。默认写入工作区根目录下的 .dagr/run.json(将其加入 gitignore),每个文件对应一次运行:运行中的并行泳道只是 deps 互不相交的任务,而独立工作流应使用单独的运行文件和单独的窗格。

用一句话说明对象模型

  • Project = 递归的可视化范围。运行是隐式根节点;子项目只是带有 parent 的项目。阶段或工作流也只是项目,而非另一种实体。每个任务有一个可选的 project 归属,而其依赖边可以跨越任意项目边界。
  • Task = 工作项。id 稳定,由你选择(绝不能用窗格 ID)。kind 是开放集合 — impl · review · test · gate · question · docs · ship · … — 选择最贴切的一种(question 用于需要由人回答的任务,gate 用于汇聚多个依赖的任务;两者都会改变 dagr 的绘制方式)。 状态:queued · working · review · blocked · done · failed · rejected · canceled · settled_unverified。
  • Attempt = 对任务的一次尝试。id 格式为 T·aN,n 从 1 开始。 状态:queued · working · done · failed · rejected · settled_unverified · lost。重试绝不会改写某次尝试 — 而是追加一次带有 cause 的新尝试。 任务的历史记录绝不会倒退 — 你不会改写或删除任何尝试。任务的状态取决于最新一次尝试,因此退回会让一个 done 任务在新尝试开启时回到 working。
  • Event = 只追加的来源记录:attempt_started · attempt_settled · promoted · directive · message_resolved · note,按 at 时间戳升序排列。绝不改写或重新排列事件。
  • 每个终态结果都带有证据等级:verified(经机械检查 — 测试运行、提交回执) · reported(来自执行者的类型化信封) · heuristic(推断所得) · asserted(未经佐证的声明)。“Agent 说已完成”最多算作 reported。缺少信封?那就是 settled_unverified — 一种真正的终态,而不是宽松意义上的 done。

dagr check 会强制遵守的不变量

  1. 任务状态是对各次尝试的投影:working 需要存在正在进行的尝试;done/rejected/settled_unverified 需要与最新尝试匹配;failed 接受最新尝试为 failed 或 lost;queued 不允许存在正在进行的尝试,也不允许最新尝试为 done 或 settled_unverified——任务在一次 failed 或 rejected 尝试后重新排队是正确且符合预期的;review 至少需要一次尝试。canceled 仅适用于任务:它会撤回计划中的工作,而不会改写或凭空创建一次尝试。
  2. 原因指向过去:第 n>1 次尝试带有 cause(sent_back · gate_failed · followup · superseded),其 ref 指向更早的一次尝试。只有 n=1 时才使用 initial。
  3. 运行是一个 DAG:deps 或 gate 的 inputs 中不能存在环。gate 的汇入集合就是其 deps;只有当汇入集合与依赖集合不同时,才使用 inputs 覆盖。将真正的顺序工作编码为依赖项;任务声明顺序仅用于在没有尝试的同级任务之间打破平局。
  4. 终态尝试必须带有 outcome,其中 result == state,并且证据等级有效;时间戳必须是有效的 ISO-8601 格式,且尝试的结束时间晚于开始时间。
  5. ID 由你指定且必须唯一:任务 ID 不能与尝试 ID 冲突。herdr 窗格 ID 应放在 locator 中,绝不能放在 id 中。
  6. 进行中的尝试必须可定位且仍然存活:进行中的尝试需要 locator({"pane": "wX:pN"})和已填充的 liveness — prompt_acknowledged(布尔值)、last_output_at(时间戳字符串)、queued_input(已输入但尚未提交的 composer 行数,若无则为 0——必须是数字,不能是布尔值;布尔值会导致整个文档被拒绝)。当你的 Agent 产生输出时,更新 last_output_at;界面会显示过时状态,沉默才是敌人。
  7. 受阻状态要指出解除阻塞的对象;晋升是一种事件,不能靠推断——当汇入完成时,发出 {"type": "promoted", "task": ...}。
  8. **项目归属不等于依赖关系。**为每个任务指定一个真实的可视化归属;将所有阻塞项都放入 deps,包括跨项目的边。绝不要为了同时显示两边的影响,就把一个任务重复放入两个项目。
  9. **操作员消息保留其权限和 ID。**窗格会向你传递一个 [DAGR OPERATOR MESSAGE] 信封。遵守 recommend_and_return 与 may_decide_and_continue 的区别;在解决事件中保留 message_id。dagr 负责传递请求,但不会替你处理。

操作示例

{
  "dagr": 3,
  "run": {
    "id": "run-myjob-v01", "title": "what this run is",
    "started_at": "2026-02-01T09:00:00Z",
    "orchestrator": {"pane": "wX:p1"}
  },
  "generated_at": "2026-02-01T09:00:00Z",
  "projects": [],
  "tasks": [],
  "events": []
}

每次写入时都刷新 generated_at——它是屏幕上所有“Nm ago”的计时基准,值过时会显示过时状态横幅。

如果可用,请自动将 run.orchestrator 设置为你自己的 $HERDR_PANE_ID(否则使用稳定的 Herdr Agent 目标)。m 会在这里排入操作员消息;不要将其指向工作器。无需用户引导步骤或额外的控制器。

在任务前规划项目和子项目

使用能够提供清晰视觉归属的最精简层级。不要创建单独的 phases 或 workstreams 数组:

"projects": [
  {"id": "APP", "title": "Application"},
  {"id": "API", "title": "API stream", "parent": "APP", "owner": "api-lead"},
  {"id": "UI", "title": "UI stream", "parent": "APP", "owner": "ui-lead"}
]

API 中的任务使用 "project": "API"。如果 UI 任务依赖它,就将 UI 任务保留在 UI 中,并将 API 任务 ID 放入其 deps;渲染器会显示跨项目依赖边。不要复制任务,也不要强行将其归入共同的父项目。只有真正属于运行级别的工作才省略 project。

先从粗略规划开始:声明有用的项目骨架、当前工作和有意义的关卡。随着发现新任务,在相关时添加;发布操作人员可见的工作,而不是每个内部 Agent、工具或运行时步骤。Dagr 根据 deps、owner/actor 和 kind 推导排队任务行的 waits、ready、unassigned 和 needs answer;绝不要将这些编码为额外字段。

打开任务并启动首次尝试
{
  "id": "L1", "title": "impl: core lane", "kind": "impl",
  "owner": "l1-dev", "state": "working", "deps": [],
  "attempts": [{
    "id": "L1·a1", "n": 1, "cause": {"type": "initial"},
    "actor": "l1-dev", "model": "fable",
    "locator": {"pane": "wX:p3"},
    "state": "working", "started_at": "2026-02-01T09:05:00Z",
    "liveness": {"prompt_acknowledged": true, "last_output_at": "2026-02-01T09:05:00Z"}
  }]
}

将 {"at": ..., "type": "attempt_started", "task": "L1", "attempt": "L1·a1", "actor": "l1-dev"} 追加到 events。

model 是一个自由字符串,会原样渲染——dagr 不会改写它,因此显示形式由你选择。使用简短的 model·effort 标签:fable·xhigh、sol5.6·max、luna5.6·max、terra5.6·max。面板为该列提供 12 个字符单元(terra5.6·max 刚好填满);更长的字符串会截断并显示省略号,而窄屏布局会先隐藏标签,不会裁切标题。

结算一次尝试(附带证据)

设置尝试的 state、ended_at 和 outcome(result 必须与 state 相同);同步任务的 state;追加一个 attempt_settled 事件。 (ended_at 是工作停止的时间;结算事件的 at 是判定得出的时间——两者可能不同,而且时间差本身包含真实信息:一个于 09:50 停止的面板,其拒绝结果于 10:12 才得出,就会同时记录这两个时间。)优先使用你掌握的最有力证据。

判定规则:等级描述的是你对结算声明所掌握的证据。done 要求有人声称已完成——无论声明多么薄弱;等级用于评估该声明的证据。当无人声称任何内容,而你是根据运行时信号(面板退出、没有响应、绿色提示符)推断时,状态应为 settled_unverified——这正是该状态的用途。四种如实的结算方式在 examples/06-evidence-tiers.json 中有完整说明:

  • verified — 机械凭据: {"result": "done", "evidence": "verified", "receipt": "cargo test 40/40 ✓ @ a1b2c3d"}
  • reported — 来自执行者的类型化信封(结构化结果数据,而非聊天文本): {"result": "done", "evidence": "reported", "receipt": "result envelope: {files: 4, status: complete}"}
  • asserted — 执行者声称已完成,但仅以文本形式声明: {"result": "done", "evidence": "asserted", "reason": "actor asserted completion in chat; no typed envelope to verify against"}
  • 无人声称任何内容 — 这不是较弱的 done,而是不同的终态 settled_unverified,并且仍需要 ended_at 和匹配的结果: "state": "settled_unverified", "ended_at": ..., "outcome": {"result": "settled_unverified", "evidence": "heuristic", "reason": "no claim of completion from the actor; inferring from clean pane exit"}。 它绝不会将未经证实的工作升级为成功。
退回并重新进入

审查者拒绝工作时会涉及三条记录——审查尝试结算为 done(审查本身成功),被审查的尝试结算为 rejected,修复轮次则是一个新尝试,其 cause 指向审查尝试:

{"id": "L1·a2", "n": 2,
 "cause": {"type": "sent_back", "by": "rev-1", "ref": "R1·a1", "reason": "error paths untested"},
 "locator": {"pane": "wX:p3"}, "state": "working", "started_at": "...",
 "liveness": {"prompt_acknowledged": true, "last_output_at": "..."}}

cause.ref 必须指向一个已声明且更早的尝试——如果文件中没有 R1·a1,检查就会失败;即使是可正常运行的重试,也仍需要定位信息和存活状态。将任务的状态设回 state: "working";dagr 会根据此 cause 绘制 ↩ 重新进入路径。完整文档: examples/03-send-back.json。

门控汇合

门控的汇合输入就是它的 deps——这样每条门控边都会纳入环路检查。只有当汇合输入集确实与依赖集不同时,才使用 inputs:

{"id": "G1", "title": "gate: merge lanes", "kind": "gate", "owner": "orchestrator",
 "project": "APP", "criteria": "API and UI reviews are clean",
 "state": "queued", "deps": ["L1", "L2", "L3"], "attempts": []}

按符合预期的人类阅读顺序声明门控输入,并确保整个 tasks 数组也符合预期:对于没有尝试记录的同级任务,以及门控中承载状态的汇合区,dagr 都会保留声明顺序。不要为了排序而重命名 id,不要把门控挂到某条泳道上作为布局变通方案,也不要添加合成的“汇合”任务。声明真实的汇合输入。带有 project 的门控是该项目中的里程碑;不带 project 时,dagr 会将它放在所有输入共同所属的最近项目中。因此,局部于 API 的门控会留在 API 中,汇合 API 和 UI 的门控位于它们的父级 APP 中,而汇合互不相关的顶层项目的门控则是运行级里程碑。输入尝试的时间戳不会决定门控的父级。每个直接输入会显示为 ○ 等待、◎ 进行中或 ● 已满足(以及常规的阻塞/待审核/失败标记)。在较窄的窗格中,这些标记会汇总显示;选中门控时,仍可查看确切的输入 id。

已知组织归属时,优先显式指定 project;从输入所属位置推断才符合实际时,则省略它。切勿仅为改变门控在屏幕上的位置而添加虚假依赖。

最后一个输入到达时,追加一条 promoted 事件——并带上它的 at 时间戳,与每条事件一样:

{"at": "...", "type": "promoted", "task": "G1", "detail": "fan-in complete: L1 ✓, L2 ✓"}

“推进门控”意味着仍须遵循投影规则:状态为 working/done 的门控需要有自身处于该状态的尝试——门控工作开始时,开启 G1·a1(如果处于活动状态,则附上定位器和存活状态),并像其他尝试一样用证据将其结算。attempts: [] 的门控可以处于 queued 状态;如果门控已撤回,则可以处于 canceled 状态。完整文档: examples/04-gate-promotion.json。

取消计划中的工作

将任务设为 "state": "canceled",并填写简短的 note;保留所有先前的尝试和事件,不作更改。不存在已取消的尝试结果。由于取消不等于成功,请更新或取消任何仍依赖该任务的任务。

声明循环策略(后续轮次),不要只暗示
"policy": {"rounds_max": 3, "futures": [
  {"on": "pass", "ref": "G1"},
  {"on": "fail", "node": {"id": "L1·a2", "title": "fix round", "actor": "l1-dev"}, "loop_back": true},
  {"on": "fail", "streak": 2,
   "node": {"id": "L1x·a1", "title": "escalate: fresh approach", "attribution": "predicted"},
   "after": "L1·a2", "source": "two-strikes rule"}
]}

ref 指向现有任务(显示为 »);node 声明尚未实际存在的节点(显示为 ○,带有 loop_back 时显示为 ⟲,当 attribution 为 predicted 时显示为 ≈);after 将后续内容接到同一策略的同级后续节点上。dagr 仅根据此区块渲染后续节点,而且只渲染处于 working 或 blocked 状态的节点。完整文档: examples/05a-policy-declared.json。

将后续节点实体化(预测的轮次实际开始时)

policy 表示当前意图,而非历史记录——这是唯一要原地编辑的区块。历史记录保存在 attempts 和 events 中;这些内容只能追加,而策略是你需要保持真实的预测。当已声明的后续节点成为现实后,请在同一份候选文档中完成以下所有操作(然后进行检查 → 重命名):

  1. 从 policy.futures 中移除已消耗的后续节点。若保留它,就会与真实尝试的 id 冲突,并被检查拒绝。
  2. 修复指向已移除节点的 after 链:原先以 "after": "L1·a2" 接续的同级后续节点,现在应直接接到任务上(删除其 after),或接到另一个仍未实现的同级节点上。
  3. 追加真实尝试,使用后续节点预测的 id,并通过 cause 指向触发项(循环返回修复轮次的 followup 应引用失败的尝试;门控退回时使用 gate_failed);如果处于活动状态,还要附上定位器和存活状态。
  4. 追加 attempt_started 事件。切勿修改先前的尝试或事件。

前后对照,两份文档均通过严格检查: examples/05a-policy-declared.json → examples/05b-policy-materialized.json。

记录人工决策
{"at": "...", "type": "directive", "verb": "reject", "by": "operator",
 "task": "L1", "detail": "error paths untested"}

动词:reject · unblock · answer · rule。指令是决策日志;聊天内容不是。

回答问题(通过指令完成任务)

依赖项已完成的排队 question 会在关注队列中显示为 needs answer;生产方不会声明单独的就绪字段。 单独的指令事件无法完成任务——任务状态是基于尝试的投影,因此人工回答需要有一次 actor 为人工的尝试。将两者写入同一个候选项:

{"id": "Q1", "title": "question: retry budget?", "kind": "question",
 "owner": "operator", "state": "done", "deps": [],
 "attempts": [{
   "id": "Q1·a1", "n": 1, "cause": {"type": "initial"},
   "actor": "operator", "state": "done",
   "started_at": "2026-02-01T09:40:00Z", "ended_at": "2026-02-01T09:42:00Z",
   "outcome": {"result": "done", "evidence": "reported",
               "receipt": "directive answer: retry budget 2 rounds"}}]}

再加上事件:

{"at": "2026-02-01T09:42:00Z", "type": "directive", "verb": "answer",
 "by": "operator", "task": "Q1", "detail": "retry budget 2 rounds"}

回执会引用答案;指令则是决策日志条目。 同样的模式也适用于任何由人工解决的任务(例如通过解除阻塞来关闭 question,或通过规则将任务退役)。完整文档: examples/07-answer-question.json。

处理操作员消息

面板的默认操作是一个带上下文的消息编辑器。Herdr 会将已完成的消息直接排入 run.orchestrator 定位器,因此无需另建收件箱守护进程。你会收到如下信封:

[DAGR OPERATOR MESSAGE]
message_id: msg-0123456789abcdef
run: run-myjob-v01
revision: 2026-02-01T09:42:00Z
target: G1
starter: get-guidance
authority: recommend_and_return

Ask sol5.6·max and fable·xhigh independently, then combine their opinions.

请执行以下操作:

  1. 确认收到消息。将原始文本视为针对指定目标的指令,并受现有运行范围约束。
  2. 不受文本内容影响,独立遵循权限: recommend_and_return 表示进行分析并返回选项; may_decide_and_continue 允许你自行决策并继续。绝不要仅凭“最佳猜测”之类的措辞推断出第二种权限。
  3. 将模型、推理和多 Agent 请求视为普通的可编辑指令,并使用常规编排工具。dagr 不会运行这些请求。
  4. 在后续处理中保留 message_id。问题解决后,追加一个事件:
{"at":"2026-02-01T09:50:00Z","type":"message_resolved","task":"G1",
 "message_id":"msg-0123456789abcdef",
 "detail":"recommended option B after two independent reviews; awaiting operator"}

如果有多条消息为同一项决策提供依据,请在指令/解决事件中设置 source_messages。不要编辑 messages.jsonl;dagr 负责维护该只追加的投递日志。你的事件是指向该日志的持久项目记忆链接。

按需自定义三个提示词起始项

没有入门步骤。内置项(Use judgment、Get guidance、Snooze)默认可用。如果用户要求添加或更改操作,请在运行文件旁以原子方式写入 actions.json:

{
  "version": 1,
  "include_defaults": true,
  "actions": [
    {"id":"architecture-council", "label":"Architecture council",
     "prompt":"Ask two independent architecture reviewers and synthesize.",
     "authority":"recommend"}
  ]
}

每个操作都只是一个预填的可编辑提示词,加上 recommend|decide 权限。列表应保持精简;优先选择一个灵活的起始项,而不是许多僵化按钮。与内置项匹配的 id 会覆盖对应内置项。include_defaults: false 会替换内置项。支持的配置格式为版本 1;最多显示九个起始项,标签上限为 80 字节,提示词上限为 32 KiB。面板会自动重新加载此文件,并在配置无效或不受支持时显示横幅。

旧版顶层运行 actions 值可读取,但不起作用。请勿添加这些值。

运行时丢失

面板在工作中途崩溃了?该尝试状态为 lost(无需结果),任务会投影为 failed 或 blocked。不要删除该尝试;追踪记录就是凭据。

请勿执行的操作

  • 不要重新编号、改写或删除尝试或事件。只追加。
  • 不要将 herdr 面板/Agent id 放入任务或尝试 id 中——它们是 locator 数据,设计上具有时效性。
  • 没有机械回执时,不要声称状态为 verified。事后不要升级证据,除非追加新事件说明原因。
  • 不要根据 herdr 对当前状态的判断来编码任务状态(面板处于运行状态 ≠ 工作已完成)。herdr 表示位置,契约表示事实。
  • 不要镜像记录每个内部 Agent/工具步骤;只纳入操作员需要了解、引导或验证的工作。
  • 不要手动计算“完成百分比”——请写入每次尝试的 progress({"done": 3, "total": 7, "note": "..."})和时间戳;分析数据通过查询获得。

字段参考

完整架构见 dagr 仓库中的 CONTRACT.md(Schema v3 部分);其中也列出了 findings 代码。当 dagr check --json 给出你不认识的代码时,请阅读其消息——每条 finding 都会附上出错字段的 JSON 路径。

评论

支持 Markdown