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 项逐条映射

-
+

13 项基线任务逐条映射

+
Lark ShortcutDWS 主入口结论DWS 视角与边界
@@ -45,10 +45,10 @@ - + - +
基线任务DWS 主入口结论DWS 视角与边界
+space-listwiki +space-list完整对齐严格空集合、游标续传、自动翻页、停滞检测;支持组织/我的知识库。
+space-createwiki +space-create超过公开真实 32 字符名称上限;创建后按 workspaceId 读回。
+delete-spacewiki +delete-space超过预读目标、高风险确认、只接受 success=true;兼容 +space-delete。
+member-listwiki +member-list任务对齐严格成员数组、角色过滤、真实上限 50;后端无游标,不能提供诚实的 page-all。
+member-removewiki +member-remove任务对齐批量 userId,移除后读回确认成员已消失;身份类型边界同上。
+node-listwiki +node-list完整对齐正确跨域路由 doc/list_nodes,严格空目录、分页与自动翻页。
+node-getwiki +node-get任务对齐支持 DingTalk 节点 ID/在线文档 URL并返回文档域元数据;不接受 Lark 特有 token/type 组合。
+node-getwiki +node-get任务对齐支持 DingTalk 节点 ID/在线文档 URL 并返回文档域元数据;不接受跨平台专用的 token/type 组合。
+node-createwiki +node-create任务对齐支持 adoc/axls/able/appt/adraw/amind/folder 并读回;无 origin/shortcut 等价接口。
+node-copywiki +node-copy超过确认后要求新 nodeId 并读取副本;底层面向在线节点,不把 .dlink 当独立副本。
+movewiki +move任务对齐同一入口支持 Wiki 内移动和“我的文档”在线节点入 Wiki,读回 workspace/folder;无 Lark apply 权限迁移开关。
+movewiki +move任务对齐同一入口支持 Wiki 内移动和“我的文档”在线节点入 Wiki,读回 workspace/folder;底层接口没有 apply 权限迁移开关。
+move-to-drivewiki +move-to-drive超过DWS 当前接口同步完成并读回 workspace 变化,无需暴露异步 task 轮询。
+node-deletewiki +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")})