diff --git a/.changes/wiki-shortcut-alignment.md b/.changes/wiki-shortcut-alignment.md
index 356fb40d..86551e85 100644
--- a/.changes/wiki-shortcut-alignment.md
+++ b/.changes/wiki-shortcut-alignment.md
@@ -4,5 +4,5 @@ category: Added
- **Wiki Shortcut workflows** — publishes 20 reviewed space, member, node, and
activity shortcuts with strict collection validation, cursor handling,
- write read-backs, Lark-compatible task routing, and documented backend
+ write read-backs, task-oriented routing, and documented backend
boundaries.
diff --git a/docs/shortcut-public-catalog.json b/docs/shortcut-public-catalog.json
index eef23b40..b74e3973 100644
--- a/docs/shortcut-public-catalog.json
+++ b/docs/shortcut-public-catalog.json
@@ -1,5 +1,5 @@
{
- "generated_at": "2026-08-14T10:41:08.959563",
+ "generated_at": "2026-08-14T11:59:55.882207",
"count": 418,
"results": [
{
@@ -3675,7 +3675,7 @@
"risk": "read",
"status": "reviewed_available",
"disposition": "semantic_adapter",
- "semantic_delta": "补充 Lark Shortcut 缺失的知识库协作动态查询,严格验证 feeds 并保留游标。",
+ "semantic_delta": "补充知识库协作动态查询,严格验证 feeds 并保留游标。",
"availability": "available"
},
{
@@ -3715,7 +3715,7 @@
"risk": "write",
"status": "reviewed_available",
"disposition": "primary_smart",
- "semantic_delta": "补充 Lark Shortcut 缺失的成员角色更新,并执行终态与列表读回校验。",
+ "semantic_delta": "补充成员角色更新,并执行终态与列表读回校验。",
"availability": "available"
},
{
@@ -3795,7 +3795,7 @@
"risk": "read",
"status": "reviewed_available",
"disposition": "semantic_adapter",
- "semantic_delta": "补充 Lark Wiki Shortcut 缺失的库内关键词和扩展名搜索,并拒绝假空结果。",
+ "semantic_delta": "补充库内关键词和扩展名搜索,并拒绝假空结果。",
"availability": "available"
},
{
@@ -3825,7 +3825,7 @@
"risk": "read",
"status": "reviewed_available",
"disposition": "schema_leaf",
- "semantic_delta": "提供 Lark Shortcut 未单列的知识库详情入口,并要求 workspaceId 业务证据。",
+ "semantic_delta": "补充知识库详情入口,并要求 workspaceId 业务证据。",
"availability": "available"
},
{
diff --git a/docs/wiki-shortcut-business-review.html b/docs/wiki-shortcut-business-review.html
index 4d7c871e..59a36df8 100644
--- a/docs/wiki-shortcut-business-review.html
+++ b/docs/wiki-shortcut-business-review.html
@@ -18,26 +18,26 @@
DWS Wiki Shortcut 全景评审
- 以 lark-cli 1.0.78 的 13 个 Wiki Shortcut 为任务基线,重新审视 DWS 的空间、成员、节点与动态能力。本次不是按命令名凑数:每个入口都要求真实业务证据,缺失数组、畸形响应、空确认或读回不一致一律失败。
+ 以 13 项成熟 Wiki 用户任务为基线,重新审视 DWS 的空间、成员、节点与动态能力。本次不是按命令名凑数:每个入口都要求真实业务证据,缺失数组、畸形响应、空确认或读回不一致一律失败。
评审日期 2026-08-14独立 worktree / 独立分支真实组织数据 E2E报告已去标识化
20公开 Wiki Shortcuts
-
13/13Lark 用户任务有对应路径
+
13/13基线用户任务有对应路径
7DWS 额外场景
20/20真实数据能力已触达
结论先行
- DWS 已形成比“API 快捷别名”更完整的 Wiki 任务层。 Lark 的 13 个用户任务均有对应入口;其中 DWS 额外提供空间搜索/详情/唯一解析、成员角色更新、库内节点搜索、协作动态和按空间名新建文档。创建、复制、移动等关键写能力从“请求发出”升级为“终态 + ID + 读回”成功标准。
- 不能假装完全同构。 DingTalk 成员接口不提供游标,单次真实上限是 50,因此无法诚实实现 Lark 的成员 --page-all;成员身份只接受同组织可用的 userId,不能对齐 Lark 的 email/open_id 等多种 member-id-type;节点创建也没有等价的 origin/shortcut 模式。这些差异保留为明确边界,而不是用本地循环或空结果伪装。
+ DWS 已形成比“API 快捷别名”更完整的 Wiki 任务层。 基线中的 13 个用户任务均有对应入口;DWS 还提供空间搜索/详情/唯一解析、成员角色更新、库内节点搜索、协作动态和按空间名新建文档。创建、复制、移动等关键写能力从“请求发出”升级为“终态 + ID + 读回”成功标准。
+ 能力边界必须诚实表达。 DingTalk 成员接口不提供游标,单次真实上限是 50,因此不能实现成员 --page-all;成员身份只接受同组织可用的 userId,无法提供 email/open_id 等多种身份模式;节点创建也没有等价的 origin/shortcut 模式。这些差异保留为明确边界,而不是用本地循环或空结果伪装。
- Lark 13 项逐条映射
- | Lark Shortcut | DWS 主入口 | 结论 | DWS 视角与边界 |
+ 13 项基线任务逐条映射
+ | 基线任务 | DWS 主入口 | 结论 | DWS 视角与边界 |
+space-list | wiki +space-list | 完整对齐 | 严格空集合、游标续传、自动翻页、停滞检测;支持组织/我的知识库。 |
+space-create | wiki +space-create | 超过 | 公开真实 32 字符名称上限;创建后按 workspaceId 读回。 |
+delete-space | wiki +delete-space | 超过 | 预读目标、高风险确认、只接受 success=true;兼容 +space-delete。 |
@@ -45,10 +45,10 @@
+member-list | wiki +member-list | 任务对齐 | 严格成员数组、角色过滤、真实上限 50;后端无游标,不能提供诚实的 page-all。 |
+member-remove | wiki +member-remove | 任务对齐 | 批量 userId,移除后读回确认成员已消失;身份类型边界同上。 |
+node-list | wiki +node-list | 完整对齐 | 正确跨域路由 doc/list_nodes,严格空目录、分页与自动翻页。 |
- +node-get | wiki +node-get | 任务对齐 | 支持 DingTalk 节点 ID/在线文档 URL并返回文档域元数据;不接受 Lark 特有 token/type 组合。 |
+ +node-get | wiki +node-get | 任务对齐 | 支持 DingTalk 节点 ID/在线文档 URL 并返回文档域元数据;不接受跨平台专用的 token/type 组合。 |
+node-create | wiki +node-create | 任务对齐 | 支持 adoc/axls/able/appt/adraw/amind/folder 并读回;无 origin/shortcut 等价接口。 |
+node-copy | wiki +node-copy | 超过 | 确认后要求新 nodeId 并读取副本;底层面向在线节点,不把 .dlink 当独立副本。 |
- +move | wiki +move | 任务对齐 | 同一入口支持 Wiki 内移动和“我的文档”在线节点入 Wiki,读回 workspace/folder;无 Lark apply 权限迁移开关。 |
+ +move | wiki +move | 任务对齐 | 同一入口支持 Wiki 内移动和“我的文档”在线节点入 Wiki,读回 workspace/folder;底层接口没有 apply 权限迁移开关。 |
+move-to-drive | wiki +move-to-drive | 超过 | DWS 当前接口同步完成并读回 workspace 变化,无需暴露异步 task 轮询。 |
+node-delete | wiki +node-delete | 超过 | 预读并核对 workspace,高风险确认,要求删除终态。 |
diff --git a/internal/shortcut/semantic_catalog_wiki.json b/internal/shortcut/semantic_catalog_wiki.json
index 428438ee..17a3e4a1 100644
--- a/internal/shortcut/semantic_catalog_wiki.json
+++ b/internal/shortcut/semantic_catalog_wiki.json
@@ -6,25 +6,25 @@
"+space-list": {"disposition":"semantic_adapter","semantic_delta":"严格区分显式空知识库列表与缺失、畸形或内部错误响应,并保留真实分页证据。","risk":"read","public":true,"reviewed":true},
"+space-search": {"disposition":"semantic_adapter","semantic_delta":"按关键词搜索知识库并拒绝把缺失业务数组误报为零命中。","risk":"read","public":true,"reviewed":true},
"+resolve-space": {"disposition":"primary_smart","semantic_delta":"把关键词搜索收敛为唯一 workspaceId;零命中与多命中显式分流,绝不猜测。","risk":"read","public":true,"reviewed":true},
- "+space-get": {"disposition":"schema_leaf","semantic_delta":"提供 Lark Shortcut 未单列的知识库详情入口,并要求 workspaceId 业务证据。","risk":"read","public":true,"reviewed":true},
+ "+space-get": {"disposition":"schema_leaf","semantic_delta":"补充知识库详情入口,并要求 workspaceId 业务证据。","risk":"read","public":true,"reviewed":true},
"+space-create": {"disposition":"primary_smart","semantic_delta":"创建后要求 workspaceId 并通过空间详情读回验证真实落库。","risk":"write","public":true,"reviewed":true},
"+delete-space": {"disposition":"semantic_adapter","semantic_delta":"删除前读取影响目标,经高风险确认后只接受 success=true 终态。","risk":"high-risk-write","public":true,"reviewed":true},
"+member-add": {"disposition":"primary_smart","semantic_delta":"批量授予 Wiki 容器角色,严格校验写终态并读取成员列表验证响应链。","risk":"write","public":true,"reviewed":true},
- "+member-update": {"disposition":"primary_smart","semantic_delta":"补充 Lark Shortcut 缺失的成员角色更新,并执行终态与列表读回校验。","risk":"write","public":true,"reviewed":true},
+ "+member-update": {"disposition":"primary_smart","semantic_delta":"补充成员角色更新,并执行终态与列表读回校验。","risk":"write","public":true,"reviewed":true},
"+member-list": {"disposition":"semantic_adapter","semantic_delta":"严格验证成员数组并公开真实单次上限 50;后端无游标时不伪造 page-all。","risk":"read","public":true,"reviewed":true},
"+member-remove": {"disposition":"primary_smart","semantic_delta":"批量移除容器访问,严格校验写终态并读取成员列表验证响应链。","risk":"write","public":true,"reviewed":true},
"+node-list": {"disposition":"semantic_adapter","semantic_delta":"严格区分显式空目录与假空成功,稳定投影节点并保留 nextCursor/hasMore。","risk":"read","public":true,"reviewed":true},
"+node-get": {"disposition":"schema_leaf","semantic_delta":"统一节点 ID 或在线文档 URL 的元数据读取,补充文档域属性视角。","risk":"read","public":true,"reviewed":true},
- "+node-search": {"disposition":"semantic_adapter","semantic_delta":"补充 Lark Wiki Shortcut 缺失的库内关键词和扩展名搜索,并拒绝假空结果。","risk":"read","public":true,"reviewed":true},
+ "+node-search": {"disposition":"semantic_adapter","semantic_delta":"补充库内关键词和扩展名搜索,并拒绝假空结果。","risk":"read","public":true,"reviewed":true},
"+node-create": {"disposition":"primary_smart","semantic_delta":"支持七种 Wiki 节点类型,创建后要求 nodeId 并读取元数据验证。","risk":"write","public":true,"reviewed":true},
"+node-copy": {"disposition":"primary_smart","semantic_delta":"高风险确认后复制,必须取得新 nodeId 并读回副本,避免空响应被当作成功。","risk":"high-risk-write","public":true,"reviewed":true},
"+move": {"disposition":"primary_smart","semantic_delta":"同一入口覆盖 Wiki 内移动和我的文档在线节点入 Wiki,并校验目标 workspace/folder。","risk":"write","public":true,"reviewed":true},
"+move-to-drive": {"disposition":"primary_smart","semantic_delta":"同步移动 Wiki 节点到我的文档并读回验证 workspace 变化,免去异步任务轮询。","risk":"write","public":true,"reviewed":true},
"+node-delete": {"disposition":"semantic_adapter","semantic_delta":"删除前读取并核对 workspace,经高风险确认后要求 success=true 终态证据。","risk":"high-risk-write","public":true,"reviewed":true},
- "+feed-list": {"disposition":"semantic_adapter","semantic_delta":"补充 Lark Shortcut 缺失的知识库协作动态查询,严格验证 feeds 并保留游标。","risk":"read","public":true,"reviewed":true},
+ "+feed-list": {"disposition":"semantic_adapter","semantic_delta":"补充知识库协作动态查询,严格验证 feeds 并保留游标。","risk":"read","public":true,"reviewed":true},
"+wiki-new-doc": {"disposition":"primary_smart","semantic_delta":"按空间名精确唯一解析、创建在线文档并读回验证;零命中和歧义均显式失败。","risk":"write","public":true,"reviewed":true}
}
}
diff --git a/internal/shortcut/wiki/nodes.go b/internal/shortcut/wiki/nodes.go
index 0655992c..c78cc6b8 100644
--- a/internal/shortcut/wiki/nodes.go
+++ b/internal/shortcut/wiki/nodes.go
@@ -37,7 +37,7 @@ func nodeAliases() map[string][]string {
return map[string][]string{"nodeId": {"nodeId", "id", "dentryUuid", "fileId"}, "name": {"name", "title", "nodeName", "fileName"}, "type": {"type", "nodeType", "docType", "fileType"}, "contentType": {"contentType"}, "folderId": {"folderId", "parentId"}, "workspaceId": {"workspaceId", "spaceId"}, "url": {"docUrl", "url", "webUrl"}}
}
-var NodeGet = readShortcut("+node-get", "获取知识库节点详情", "已知节点 ID 或在线文档 URL 时读取元数据;比 Lark Wiki 仅返回节点元数据的视角额外统一文档/文件属性。", "", []shortcut.Flag{{Name: "node", Type: shortcut.FlagString, Required: true, Desc: "节点 ID 或 URL"}}, []contract.ParamDecl{{Name: "node", Property: "nodeId"}}, func(rt *shortcut.RuntimeContext) error {
+var NodeGet = readShortcut("+node-get", "获取知识库节点详情", "已知节点 ID 或在线文档 URL 时读取元数据,并在节点信息之外统一返回文档/文件属性。", "", []shortcut.Flag{{Name: "node", Type: shortcut.FlagString, Required: true, Desc: "节点 ID 或 URL"}}, []contract.ParamDecl{{Name: "node", Property: "nodeId"}}, func(rt *shortcut.RuntimeContext) error {
data, err := rt.CallMCPData("doc", "get_document_info", map[string]any{"nodeId": rt.Str("node")})
if err != nil {
return err
@@ -191,9 +191,9 @@ func executeMove(rt *shortcut.RuntimeContext, toDrive bool) error {
return rt.Output(map[string]any{"success": true, "nodeId": rt.Str("node"), "node": verified})
}
-var Move = writeShortcut("+move", "移动节点到知识库并读回验证", "将 Wiki 节点或我的文档在线节点移动到目标知识库/文件夹;同时覆盖 Lark 的 Wiki 内移动与 Drive 文档入 Wiki 场景。", shortcut.RiskWrite, wikiWriteSafety(true), []shortcut.Flag{{Name: "workspace", Type: shortcut.FlagString, Required: true, Desc: "目标知识库 ID"}, {Name: "node", Type: shortcut.FlagString, Required: true, Desc: "节点 ID"}, {Name: "folder", Type: shortcut.FlagString, Desc: "目标文件夹 ID"}}, []contract.ParamDecl{{Name: "workspace", Property: "workspaceId"}, {Name: "node", Property: "nodeId"}, {Name: "folder", Property: "targetFolderId"}}, func(rt *shortcut.RuntimeContext) error { return executeMove(rt, false) })
+var Move = writeShortcut("+move", "移动节点到知识库并读回验证", "将 Wiki 节点或我的文档在线节点移动到目标知识库/文件夹;同一入口覆盖库内移动与在线文档入库场景。", shortcut.RiskWrite, wikiWriteSafety(true), []shortcut.Flag{{Name: "workspace", Type: shortcut.FlagString, Required: true, Desc: "目标知识库 ID"}, {Name: "node", Type: shortcut.FlagString, Required: true, Desc: "节点 ID"}, {Name: "folder", Type: shortcut.FlagString, Desc: "目标文件夹 ID"}}, []contract.ParamDecl{{Name: "workspace", Property: "workspaceId"}, {Name: "node", Property: "nodeId"}, {Name: "folder", Property: "targetFolderId"}}, func(rt *shortcut.RuntimeContext) error { return executeMove(rt, false) })
-var MoveToDrive = writeShortcut("+move-to-drive", "移动 Wiki 节点到我的文档", "将 Wiki 在线节点同步移动到我的文档或指定文件夹,并以元数据读回替代 Lark 异步任务轮询。", shortcut.RiskWrite, wikiWriteSafety(true), []shortcut.Flag{{Name: "node", Type: shortcut.FlagString, Required: true, Desc: "Wiki 节点 ID"}, {Name: "folder", Type: shortcut.FlagString, Desc: "我的文档目标文件夹 ID"}}, []contract.ParamDecl{{Name: "node", Property: "nodeId"}, {Name: "folder", Property: "targetFolderId"}}, func(rt *shortcut.RuntimeContext) error { return executeMove(rt, true) })
+var MoveToDrive = writeShortcut("+move-to-drive", "移动 Wiki 节点到我的文档", "将 Wiki 在线节点同步移动到我的文档或指定文件夹,并以元数据读回证明任务完成。", shortcut.RiskWrite, wikiWriteSafety(true), []shortcut.Flag{{Name: "node", Type: shortcut.FlagString, Required: true, Desc: "Wiki 节点 ID"}, {Name: "folder", Type: shortcut.FlagString, Desc: "我的文档目标文件夹 ID"}}, []contract.ParamDecl{{Name: "node", Property: "nodeId"}, {Name: "folder", Property: "targetFolderId"}}, func(rt *shortcut.RuntimeContext) error { return executeMove(rt, true) })
var NodeDelete = writeShortcut("+node-delete", "删除知识库节点", "明确确认后将节点移入回收站;先读取目标,且删除响应必须提供 success=true 终态证据。", shortcut.RiskHighWrite, wikiDeleteSafety(), []shortcut.Flag{{Name: "workspace", Type: shortcut.FlagString, Required: true, Desc: "知识库 ID,用于确认影响范围"}, {Name: "node", Type: shortcut.FlagString, Required: true, Desc: "节点 ID"}}, []contract.ParamDecl{{Name: "node", Property: "nodeId"}}, func(rt *shortcut.RuntimeContext) error {
preflight, err := rt.CallMCPData("doc", "get_document_info", map[string]any{"nodeId": rt.Str("node")})