Skip to content

feat(controller): mermaid workflow rendering + task-level inspection API - #1230

Merged
shiyiyue1102 merged 2 commits into
agentscope-ai:mainfrom
LUOSENGWA:feat/workflow-observability
Sep 10, 2026
Merged

feat(controller): mermaid workflow rendering + task-level inspection API#1230
shiyiyue1102 merged 2 commits into
agentscope-ai:mainfrom
LUOSENGWA:feat/workflow-observability

Conversation

@LUOSENGWA

Copy link
Copy Markdown
Contributor

Summary

Part 1 (read-only observability) of the workflow engine design in
#1223: no new
writes, no schema changes — only new read paths and one shared renderer.

  1. GET /api/v1/projects/{id}/workflow?format=mermaid — the existing
    LangGraph-aligned workflow snapshot rendered as a Mermaid flowchart
    (text/plain), so any Mermaid-capable UI (dashboards, plugins, docs) can
    embed a live DAG without re-implementing graph layout. Unknown
    format values return 400; the default JSON response is byte-identical
    to before.
  2. GET /api/v1/projects/{id}/tasks/{taskId} — node-level inspection:
    the task's graph node (status / assignee / dependencies), its TaskMeta
    (spec / summary / result / deliverables), the append-only transition
    history[] (populated once the transition engine lands — see Design: Project workflow engine — transition event stream, node-level state inspection, and graph export #1223), and
    a trace hint naming the tracing attributes worker entry spans already
    carry (agentteams.project.id / agentteams.task.id).
  3. Shared Mermaid renderer (internal/workflow) — the agt --mermaid
    CLI and the new API format render from the same code. Node labels keep
    name: status; next/ready nodes get the ready highlight and every
    other node is now colored by status (pending / delegated /
    inProgress / completed / revision / blocked). All classDefs are
    emitted so the graph renders standalone (previously only ready).

What's included

  1. internal/workflow (new package, zero dependencies): Snapshot /
    Node / Edge types + RenderMermaid — usable by both the controller
    API and the agt CLI without the CLI importing the (K8s-heavy) server
    package
  2. GET /api/v1/projects/{id}/workflow?format=mermaid — same access model
    as the JSON workflow endpoint (single K8s List for prefix resolution +
    access check; 404 existence-hiding for scoped callers); includeTasks
    is ignored for mermaid (rendering needs only nodes/edges/next)
  3. GET /api/v1/projects/{id}/tasks/{taskId} — same access model as the
    existing artifact endpoint: isSafeTaskID validation, graph membership
    check (a task id from another project → 404), owning-scope-only TaskMeta
    read (a team project never falls back to a global TaskMeta — same rule as
    tasks_detail), ownership check (task_id + project_id must match)
  4. taskDetail gains an omitempty history field, so
    ?includeTasks=true also exposes transition history once it exists
    (additive; existing consumers unaffected)
  5. agt get projects <id> --mermaid now uses the shared renderer (output
    gains status classes; structure unchanged)
  6. Docs: docs/usage/project-workflow-api.md + docs/zh-cn/usage/...
    format parameter, the new inspection endpoint, and the CLI change
    (also removes two stale internal code references from the zh-cn
    headings)

Behavior

Request Before After
GET .../workflow (no params) JSON snapshot byte-identical
GET .../workflow?includeTasks=true JSON + tasks_detail unchanged + each detail may carry history (omitempty)
GET .../workflow?format=mermaid 400 (param ignored → JSON) text/plain Mermaid flowchart
GET .../workflow?format=bogus JSON (param ignored) 400
GET .../tasks/{taskId} — (no route) 200 node-level inspection / 400 invalid id / 404 not-in-graph / 500 storage
agt get projects <id> --mermaid Mermaid, ready class only Mermaid, ready + per-status classes

Tests

  • internal/workflow: renderer unit tests — status classes, ready
    override, empty graph, all classDefs present (4 tests)
  • internal/server (8 new, 0 regressions in the full package suite):
    • mermaid format: content-type / node labels / ready override / edges /
      classDefs; format=bogus → 400
    • task inspection: full detail + parsed history (malformed entries
      skipped) + dependencies + trace hint; missing TaskMeta → node summary
      only (normalized status); task not in graph → 404; invalid task id →
      400; loop-plan task; cross-scope no-fallback (team project must not
      leak a global TaskMeta)
  • cmd/agt: CLI integration test — JSON → Snapshot → render (the exact
    --mermaid path)
  • Verified: go build / go vet / go test green on
    internal/server, internal/workflow, cmd/agt; gofmt clean

Compatibility

  • Read-only PR: no object-storage writes, no CRD changes, no TeamHarness /
    worker-runtime changes
  • Default JSON responses are byte-identical (guarded by the existing
    package test suite, unmodified)
  • New fields are all omitempty; the new route does not shadow the
    existing .../tasks/{taskId}/artifact route

摘要

工作流引擎设计(#1223)的第 1 部分(只读可观测):不新增写入、不改 schema,只加读路径和一个共享渲染器。

  1. GET /api/v1/projects/{id}/workflow?format=mermaid —— 现有 LangGraph 对齐工作流快照渲染为 Mermaid 流程图(text/plain),任何支持 Mermaid 的 UI(dashboard/插件/文档)可直接嵌入实时 DAG,无需自研图布局。未知 format 返回 400;默认 JSON 响应字节级不变。
  2. GET /api/v1/projects/{id}/tasks/{taskId} —— 节点级检视:任务的图节点(状态/负责人/依赖)、TaskMeta(spec/摘要/结果/交付物)、append-only 状态迁移 history[](状态机落地后由 Design: Project workflow engine — transition event stream, node-level state inspection, and graph export #1223 的设计填充),以及 trace 提示(worker entry span 已携带 agentteams.project.id / agentteams.task.id 属性)。
  3. 共享 Mermaid 渲染器internal/workflow)—— agt --mermaid CLI 与 API 新 format 走同一份代码。节点标签保持 name: status;next/ready 节点高亮 ready,其余节点按状态着色(pending / delegated / inProgress / completed / revision / blocked)。所有 classDef 均输出,图可独立渲染(此前仅 ready)。

包含内容

  1. internal/workflow(新包,零依赖):Snapshot / Node / Edge 类型 + RenderMermaid —— controller API 与 agt CLI 共用,CLI 无需导入(含 K8s 依赖的)server 包
  2. GET .../workflow?format=mermaid —— 与 JSON workflow 端点相同访问模型(单次 K8s List 完成前缀解析 + 鉴权;限定读者 404 隐藏存在性);mermaid 忽略 includeTasks(渲染只需 nodes/edges/next)
  3. GET .../tasks/{taskId} —— 与现有 artifact 端点相同访问模型:isSafeTaskID 校验、图成员检查(其他项目的 task id → 404)、仅从项目所属 scope 读 TaskMeta(team 项目绝不回退 global TaskMeta —— 与 tasks_detail 同规则)、所有权校验(task_id + project_id 必须匹配)
  4. taskDetail 增加 omitempty history 字段 —— ?includeTasks=true 同样暴露迁移历史(纯增量,既有消费者不受影响)
  5. agt get projects <id> --mermaid 改走共享渲染器(输出增加状态 class,结构不变)
  6. 文档:project-workflow-api.md 中英双份 —— format 参数、新检视端点、CLI 变更(顺带清除 zh-cn 标题中两处过时的内部代号引用)

测试

  • internal/workflow:渲染器单测 —— 状态 class、ready 覆盖、空图、classDef 齐全(4 个)
  • internal/server(8 个新测试,全包 0 回归):mermaid format(content-type / 节点标签 / ready 覆盖 / 边 / classDef;format=bogus → 400);task 检视(完整 detail + 解析 history(畸形条目跳过)+ 依赖 + trace 提示;TaskMeta 缺失 → 仅节点摘要(归一化状态);任务不在图中 → 404;非法 task id → 400;loop 计划任务;跨 scope 不回退(team 项目不得泄漏 global TaskMeta)
  • cmd/agt:CLI 集成测试 —— JSON → Snapshot → 渲染(--mermaid 实际路径)
  • 验证:go build / go vet / go testinternal/serverinternal/workflowcmd/agt)全绿;gofmt 干净

兼容性

  • 只读 PR:不写对象存储、不改 CRD、不动 TeamHarness / worker runtime
  • 默认 JSON 响应字节级不变(既有包测试套件未改动、全绿守护)
  • 新增字段全部 omitempty;新路由不与既有 .../tasks/{taskId}/artifact 路由冲突

- Add shared workflow.Snapshot + RenderMermaid (internal/workflow): the
  agt CLI --mermaid and the new API format both render from one renderer.
  Node labels carry status; next/ready nodes get the ready highlight,
  other nodes a status class (pending/delegated/inProgress/completed/
  revision/blocked); all classDefs emitted for standalone rendering.
- GET /api/v1/projects/{id}/workflow?format=mermaid returns the same
  snapshot as text/plain mermaid (unknown format -> 400).
- GET /api/v1/projects/{id}/tasks/{taskId} (node-level inspection):
  graph node + TaskMeta (spec/summary/result/deliverables) + append-only
  transition history (task meta history[], cap 50; empty until the
  transition engine lands, design agentscope-ai#1223) + trace hint (worker entry
  spans carry agentteams.project.id/task.id). Same access model as the
  artifact endpoint: isSafeTaskID, graph membership, W4 404
  existence-hiding, owning-scope-only TaskMeta (no cross-scope fallback).
- taskDetail gains an omitempty history field, so ?includeTasks=true
  exposes the same audit once transitions are recorded.
- Docs: project-workflow-api.md (en + zh-cn) cover both endpoints and
  the --mermaid renderer change.
@shiyiyue1102

Copy link
Copy Markdown
Collaborator

Thanks for extending the workflow inspection surface. The project-scoped authorization and the no-global-fallback lookup in the task detail path look aligned with the existing API boundaries.

Before this moves out of draft, please address the following:

  1. RenderMermaid renders task names supplied by the workflow input. Please add coverage for Mermaid-sensitive titles (at least quotes, newlines, brackets, and edge-like syntax) and make the renderer emit valid, unambiguous Mermaid for them. This is important because task titles are user-controlled and a malformed title must not alter the rendered graph.
  2. The documentation says the trace object only carries tracing attribute names, while the response example/API returns project_id and task_id values. Please make the wording and the response contract consistent.
  3. Please resolve or explain the failing integration-tests (controller-cr, SHARD_C_TESTS, qwenpaw, hermes, false) check before requesting review. The separate translate failure looks like automation/infrastructure, but it should be retried or cleared as well.

Once these are addressed and CI is green, please mark the PR ready for review.

Reviewer follow-up on agentscope-ai#1230:

- RenderMermaid now sanitizes user-controlled titles and task ids so a
  malformed title can never alter the rendered graph structure:
  newlines -> <br>, double quotes -> #quot;, backslashes dropped,
  control chars -> space; task ids with characters outside
  [A-Za-z0-9_-] map to a collision-safe node id (labels keep the
  original text). New safety tests cover the reviewer-requested cases
  (quotes, newlines, brackets, edge-like syntax, control characters,
  unicode - 7 malformed-title cases) plus a structure-invariant test
  (a hostile title must not create extra nodes or edges) and an
  id-collision test (t.1 and t_1 stay distinct).
- Docs (en + zh-cn) + taskTraceHint comment: the trace object is a
  tracing-backend filter hint whose project_id/task_id are the values
  for the agentteams.project.id / agentteams.task.id span attributes
  (previously worded as 'attribute names' while returning values).
@LUOSENGWA
LUOSENGWA marked this pull request as ready for review September 9, 2026 13:48
@LUOSENGWA

Copy link
Copy Markdown
Contributor Author

@shiyiyue1102
Thanks for the review — all three points are addressed in 707b019d:

1. Mermaid-sensitive titles — fixed.
RenderMermaid now sanitizes user-controlled content before rendering, so a malformed title can never alter the graph structure:

  • newlines / carriage returns → <br> (mermaid's line-break tag) — a label can never spill onto another node line
  • double quotes → #quot; (mermaid's documented entity) — a title can never terminate the label string
  • backslashes dropped (a trailing one could otherwise act as an escape introducer and swallow the closing quote), other control characters → space
  • task ids containing characters outside [A-Za-z0-9_-] (e.g. dots, which isSafeTaskID allows) are mapped to a collision-safe node id; labels keep the original text

Added safety tests for exactly the requested cases — quotes, newlines, brackets, edge-like syntax (A --> B) — plus a structure-invariant test (hostile title x --> y\nz["quoted"] must render exactly one node and must not create extra edges) and an id-collision test (t.1 + t_1 stay distinct). The CLI --mermaid path uses the same shared renderer, so it's covered by the same change.

2. trace wording — fixed.
The docs (en + zh-cn) and the taskTraceHint comment now state the actual contract: trace is a tracing-backend filter hint — its project_id / task_id are the values to match against the span attributes agentteams.project.id / agentteams.task.id, which worker entry spans already carry. No backend URL is constructed.

3. CI — green on 707b019d.
All checks on the new commit are passing: 17 checks succeeded with zero failures (the two matrix jobs build-openclaw-base / release-baseline are no-op'd as out of scope for this diff). Specifically:

  • the previously failing integration-tests (controller-cr, SHARD_C_TESTS, qwenpaw, hermes, false) passed on the new commit, consistent with the flake assessment;
  • the translate check did not re-trigger on the fix commit (the PR body was unchanged), so the earlier automation failure no longer applies to the head commit.

For the record on the hermes-shard failure: it was test-23-runtime-switch failing "CoPaw runtime state was not persisted to MinIO" — the test polls 60s (5s interval) for the controller to mirror worker state files into MinIO, and the two later assertion failures cascade from that single timeout. On that same commit the same test passed in the other 3 shards that run it (openclaw/openclaw 02:17, copaw/copaw 02:19, qwenpaw/qwenpaw 02:19 UTC; hermes started latest and failed at 02:21), and this PR is read-only (new read endpoints + shared renderer + docs) with zero overlap with the worker-state mirroring path. The identical "CoPaw state 60s timeout" signature previously flaked on the openclaw shard in an earlier PR's CI (noted in #1172).

The PR is marked ready for review. Happy to address anything else.


感谢 review——三点已在 707b019d 全部处理:

1. Mermaid 敏感标题——已修复。
RenderMermaid 现在在渲染前对用户可控内容做归一化,畸形标题不可能改变图结构:

  • 换行/回车 → <br>(mermaid 的换行标签)——标签不可能溢出到别的节点行
  • 双引号 → #quot;(mermaid 文档化实体)——标题不可能提前终止标签字符串
  • 反斜杠丢弃(末尾反斜杠可能被当作转义引导符吞掉闭合引号),其他控制字符 → 空格
  • [A-Za-z0-9_-] 之外字符的 task id(如 isSafeTaskID 允许的句点)映射为防冲突节点 id;标签保留原文

新增了覆盖所要求全部用例的安全测试——引号、换行、括号、edge 语法(A --> B)——外加结构不变式测试(恶意标题 x --> y\nz["quoted"] 必须只渲染 1 个节点、不产生额外边)和 id 冲突测试(t.1 + t_1 保持不同)。CLI --mermaid 走同一共享渲染器,同一改动覆盖。

2. trace 措辞——已修复。
文档(en + zh-cn)与 taskTraceHint 注释现在表述实际契约:trace 是 tracing 后端的过滤提示——其 project_id / task_id 是用于匹配 span 属性 agentteams.project.id / agentteams.task.id(worker entry span 已携带这两个属性)。不构造任何后端 URL。

3. CI——707b019d 上已全绿。
新 commit 上所有 checks 通过:17 项 success、零失败(build-openclaw-base / release-baseline 两个 matrix job 因不在本 diff 范围内自动 no-op)。具体:

  • 此前失败的 integration-tests (controller-cr, SHARD_C_TESTS, qwenpaw, hermes, false) 在新 commit 上通过,与 flake 判断一致;
  • translate 检查未在修复 commit 上重新触发(PR body 未变),之前的自动化失败不再适用于 head commit。

hermes shard 失败记录在案:失败的是 test-23-runtime-switch 报 "CoPaw runtime state was not persisted to MinIO"——该测试以 60s 窗口(5s 间隔)轮询 controller 把 worker 状态文件镜像进 MinIO,后面两个断言失败由这一个超时级联而来。同一 commit 上同一测试在另外 3 个跑它的 shard 全部通过(openclaw/openclaw 02:17、copaw/copaw 02:19、qwenpaw/qwenpaw 02:19 UTC;hermes 最晚启动并于 02:21 失败),且本 PR 是只读的(新增只读端点 + 共享渲染器 + 文档),与 worker 状态镜像路径零交集。完全相同的 "CoPaw 状态 60s 超时" 特征此前在另一个 PR 的 CI 中于 openclaw shard 出现过 flake(#1172 有记录)。

PR 已转为 ready for review,如有其他意见随时提出。

@shiyiyue1102 shiyiyue1102 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. The updated Mermaid renderer safely handles user-controlled labels and task IDs, the task inspection endpoint preserves the existing project/team access boundary, and the trace contract is now documented consistently. The current head 707b019d has no failing or pending checks.

@shiyiyue1102
shiyiyue1102 merged commit 5bbba47 into agentscope-ai:main Sep 10, 2026
19 checks passed
LUOSENGWA added a commit to LUOSENGWA/HiClaw that referenced this pull request Sep 10, 2026
matrix.Client gained SendNotification in main (agentscope-ai#1230 task inspection
API); the team provisioner test fake no longer satisfied the
interface. Sync the fake so the package builds against current main.
LUOSENGWA added a commit to LUOSENGWA/HiClaw that referenced this pull request Sep 11, 2026
matrix.Client gained SendNotification in main (agentscope-ai#1230 task inspection
API); the team provisioner test fake no longer satisfied the
interface. Sync the fake so the package builds against current main.
LUOSENGWA added a commit to LUOSENGWA/HiClaw that referenced this pull request Sep 11, 2026
matrix.Client gained SendNotification in main (agentscope-ai#1230 task inspection
API); the team provisioner test fake no longer satisfied the
interface. Sync the fake so the package builds against current main.
LUOSENGWA added a commit to LUOSENGWA/HiClaw that referenced this pull request Sep 12, 2026
matrix.Client gained SendNotification in main (agentscope-ai#1230 task inspection
API); the team provisioner test fake no longer satisfied the
interface. Sync the fake so the package builds against current main.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants