draftgo-cli 3.0.56 → 4.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +169 -297
  2. package/package.json +12 -7
  3. package/resources/custom-service-sdk/ai.go +520 -0
  4. package/resources/custom-service-sdk/ai_test.go +156 -0
  5. package/resources/custom-service-sdk/billing.go +596 -0
  6. package/resources/custom-service-sdk/billing_test.go +150 -0
  7. package/resources/custom-service-sdk/go.mod +3 -0
  8. package/resources/custom-service-sdk/manifest.json +72 -0
  9. package/resources/custom-service-sdk/platform.go +360 -0
  10. package/resources/custom-service-sdk/platform_logger_test.go +24 -0
  11. package/resources/custom-service-sdk/registration_test.go +39 -0
  12. package/resources/custom-service-sdk/resources.go +246 -0
  13. package/resources/custom-service-sdk/resources_billing_test.go +115 -0
  14. package/resources/custom-service-sdk/resources_files_test.go +57 -0
  15. package/resources/custom-service-sdk/resources_scope_test.go +87 -0
  16. package/resources/custom-service-sdk/sdk.go +208 -0
  17. package/resources/skill/SKILL.md +36 -87
  18. package/resources/skill/init/SKILL.md +9 -14
  19. package/resources/skill/manifest.json +6 -2
  20. package/resources/skill/references/aihub.md +28 -5
  21. package/resources/skill/references/app-api.md +56 -6
  22. package/resources/skill/references/architecture.md +2 -2
  23. package/resources/skill/references/chat-sdk.md +4 -2
  24. package/resources/skill/references/checkout.md +21 -7
  25. package/resources/skill/references/custom-services.md +124 -222
  26. package/resources/skill/references/data.md +22 -6
  27. package/resources/skill/references/delivery.md +33 -0
  28. package/resources/skill/references/diagnostics.md +51 -0
  29. package/resources/skill/references/frontend.md +93 -499
  30. package/resources/skill/references/mcp.md +65 -101
  31. package/resources/skill/references/methods.md +189 -0
  32. package/resources/skill/references/modules.md +36 -8
  33. package/resources/skill/references/runtime.md +26 -3
  34. package/resources/skill/story/SKILL.md +1 -2
  35. package/src/apiContractCache.js +112 -0
  36. package/src/cli.js +24 -20
  37. package/src/commandRegistry.js +15 -12
  38. package/src/commands/api.js +41 -10
  39. package/src/commands/apiKey.js +34 -0
  40. package/src/commands/capabilities.js +93 -0
  41. package/src/commands/check.js +1 -10
  42. package/src/commands/checkout.js +1 -1
  43. package/src/commands/commit.js +1 -1
  44. package/src/commands/components.js +550 -0
  45. package/src/commands/conflict.js +1 -1
  46. package/src/commands/connect.js +18 -8
  47. package/src/commands/customService.js +22 -8
  48. package/src/commands/dataRange.js +33 -0
  49. package/src/commands/delete.js +34 -46
  50. package/src/commands/deploy.js +1 -1
  51. package/src/commands/diff.js +18 -2
  52. package/src/commands/grant.js +29 -0
  53. package/src/commands/group.js +38 -0
  54. package/src/commands/help.js +80 -51
  55. package/src/commands/init.js +6 -12
  56. package/src/commands/listTargets.js +1 -1
  57. package/src/commands/local.js +2 -6
  58. package/src/commands/map.js +145 -28
  59. package/src/commands/mcp.js +2 -2
  60. package/src/commands/reconcile.js +1 -1
  61. package/src/commands/role.js +32 -0
  62. package/src/commands/space.js +41 -0
  63. package/src/commands/status.js +111 -8
  64. package/src/commands/uninstall.js +3 -3
  65. package/src/commands/update.js +24 -12
  66. package/src/commands/verify.js +118 -21
  67. package/src/commands/{verifyUi.js → visualVerify.js} +28 -116
  68. package/src/commands/worklog.js +90 -0
  69. package/src/consoleEncoding.js +34 -0
  70. package/src/contractCompatibility.js +57 -0
  71. package/src/customServices.js +278 -41
  72. package/src/diffReport.js +106 -0
  73. package/src/index.js +2 -0
  74. package/src/{localdev → localRuntime}/compose.js +14 -17
  75. package/src/{localdev → localRuntime}/detect.js +1 -1
  76. package/src/{localdev → localRuntime}/index.js +22 -23
  77. package/src/{localdev → localRuntime}/mysqlClient.js +1 -1
  78. package/src/{localdev → localRuntime}/services.js +28 -37
  79. package/src/mcp/client.js +11 -2
  80. package/src/mcp/protocol.js +2 -2
  81. package/src/mcp/tools.js +14 -1
  82. package/src/platforms.js +9 -0
  83. package/src/projectConfig.js +8 -4
  84. package/src/releaseInstall.js +105 -0
  85. package/src/{installers/index.js → targets.js} +3 -5
  86. package/src/updateCheck.js +48 -28
  87. package/src/worklog.js +275 -0
  88. package/src/workspaceHealth.js +1 -1
  89. package/src/worktree/backend.js +1 -1
  90. package/src/worktree/index.js +86 -51
  91. package/src/changelog.js +0 -276
  92. package/src/commands/changelog.js +0 -24
  93. package/src/commands/localDev.js +0 -9
  94. package/src/commands/sync.js +0 -46
  95. package/src/commands/task.js +0 -408
  96. package/src/commands/verifyUiCompat.js +0 -16
@@ -4,9 +4,21 @@ read_when: 编辑 pages、navigation 或 docs 正文时 · 查看 checkout manif
4
4
 
5
5
  # Checkout / Commit
6
6
 
7
+ ## 页面与内容最短流程
8
+
9
+ ```bash
10
+ draftgo map --type pages --route /admin/channel-ops --output json
11
+ draftgo checkout pages 42
12
+ draftgo diff pages 42 --stat
13
+ draftgo verify pages 42
14
+ draftgo commit pages 42
15
+ ```
16
+
17
+ 导航和文档分别替换为 `nav`、`docs`。route/title 为精确匹配,两个条件取交集;只需确认范围或 checkout 状态时使用 `map --summary`,必须浏览时使用 `--limit <1-100>`(默认 20),并仅在单一 `--type` 下用 `--cursor <opaque>` 翻页。新资源先用 `draftgo api search "create page"` 或对应内容类型定位创建 operation,describe/call 取得 ID 后再 checkout;不要用 checkout 创建资源。`verify` 通过不等于已交付,commit 返回新版本与哈希才算正文写入成功。发生 409/412 时保留冲突材料并停止提交。
18
+
7
19
  ## Workflow 2.0 scope
8
20
 
9
- The checkout set includes `pages`, `navigations`, `docs/articles`, and `custom_services`. A custom-service checkout is a directory containing exactly `service.go`, `go.mod`, `go.sum`, and `service.json`, plus one complete `.base` directory. `draftgo commit custom-services <id>` saves a cloud draft; it does not publish. Use `draftgo test custom-services <id>` for server Runner validation/execution and `draftgo publish custom-services <id>` for explicit publication. DB Meta remains a live MCP/API resource and is never checked out.
21
+ The checkout set includes `pages`, `navigations`, `docs/articles`, and `custom_services`. A custom-service checkout contains exactly `service.go` and `service.json`, plus one complete `.base` directory. Runner-managed `go.mod/go.sum` never participate in diff or commit. `draftgo commit custom-services <id>` performs the complete delivery (`commit -> validate -> publish`) and prints an explicit published confirmation. Use `draftgo test custom-services <id>` for server Runner validation/execution; `draftgo publish custom-services <id>` remains as a direct compatibility entry point. DB Meta remains a live MCP/API resource and is never checked out.
10
22
 
11
23
  `draftgo refresh <type> <id...>` is a safe checkout shortcut. It updates only a clean local worktree; local changes stop it. A successful commit keeps current local files and one base only. Cloud version storage is the sole history source.
12
24
 
@@ -32,7 +44,7 @@ Checkout 只为已存在且已确认 ID 的资源建立本地正文与 base,
32
44
  draftgo checkout <pages|nav|docs> <id...>
33
45
  draftgo commit <pages|nav|docs> <id...>
34
46
  draftgo reconcile <pages|nav|docs> <id...>
35
- draftgo diff <pages|nav|docs> <id>
47
+ draftgo diff <pages|nav|docs|custom-services> <id> [--stat|--summary]
36
48
  draftgo conflicts
37
49
  draftgo conflict show <pages|nav|docs> <id>
38
50
  draftgo conflict resolve <pages|nav|docs> <id>
@@ -41,11 +53,13 @@ draftgo conflict resolve <pages|nav|docs> <id>
41
53
  `checkout --force` 只用于用户明确允许丢弃未提交本地修改的情况。默认 checkout 检测到 worktree 文件相对
42
54
  base 已变化时必须拒绝覆盖。
43
55
 
56
+ `diff --stat` 只显示文件与增删行数;`diff --summary` 显示资源、基线版本和变化概要。两者均不输出正文 diff,先用它们确认范围,再按需展开完整 `diff`。`--output json` 时 stdout 只包含 UTF-8 JSON,诊断走 stderr。
57
+
44
58
  ## Checkout 流程
45
59
 
46
60
  1. CLI 通过 MCP `draftgo_resource_get_metadata` 取得规范类型、content_type、SHA-256、大小、版本/revision、
47
61
  ETag 和受信任的下载/提交 URL。
48
- 2. CLI 使用 `.draftgo/config.json` 中的 SAT 通过专用 HTTP 下载完整正文;SAT 不进入 MCP 参数或日志。
62
+ 2. CLI 使用 `.draftgo/config.json` 中的用户 API Key 通过专用 HTTP 下载完整正文;API Key 不进入 MCP 参数或日志。
49
63
  3. 响应体直接流式写入同目录临时文件,校验 content_type、字节数和 SHA-256。
50
64
  4. 校验成功后原子重命名到 worktree 文件,并保存相同字节的 `.base` 文件。
51
65
  5. 最后原子更新 `.draftgo/worktree/manifest.json`。失败时不得留下半截正式文件或推进 manifest。
@@ -95,14 +109,14 @@ base 已变化时必须拒绝覆盖。
95
109
  ## Commit 流程
96
110
 
97
111
  1. 读取 manifest 指向的 worktree 文件,计算当前字节数和 SHA-256;未变化时返回 `unchanged`。
98
- 2. 按 content_type 执行本地结构和内联脚本检查;页面布局或交互有变化时运行 `draftgo verify <type> <id> --url <url>`。
112
+ 2. 按 content_type 执行本地结构和内联脚本检查。
99
113
  3. 通过专用 HTTP 流式上传原始文件,携带 `If-Match`、base version/revision、content_type、长度和 SHA-256。
100
114
  4. 完整正文不得作为 MCP tool 参数发送。
101
115
  5. 底座确认 hash 和新版本后,CLI 原子更新 `.base` 与 manifest。返回 hash 不一致时不得推进基线。
102
116
 
103
- 推荐编辑顺序:主 Agent 按任务读取相关 Reference -> MCP 精确定位 -> Agent 自主设计并批量 checkout -> 独立资源按唯一 owner 并行编辑 -> 主 Agent 汇总回读 -> 统一运行一次 `draftgo check` -> `draftgo diff` -> commit -> 页面变化时用 `draftgo verify <type> <id> --remote --url <url>` 验证远端已提交版本。遇到本地正文已经等于远端、但 base/manifest 落后时,先用 `draftgo check --remote` 确认 `committed_unrecorded`,再运行 `draftgo reconcile`;不要手改 manifest。
117
+ 本地正文已经等于远端、但 base/manifest 落后时,先用 `draftgo check --remote` 确认 `committed_unrecorded`,再运行 `draftgo reconcile`;不要手改 manifest。
104
118
 
105
- 单个 commit 成功不自动写 changelog。只有整个任务统一验证且全部 commit/MCP 交付成功后,主 Agent 才执行一次 `draftgo changelog add "<完成结果>"`。任何检查失败、409/412 或交付失败都不得写入。
119
+ 单个 commit 成功不自动完成 worklog 项。只有整个事项统一验证且全部 commit/MCP 交付成功后,主 Agent 才执行 `draftgo work complete <编号> --note "<完成结果>"`。任何检查失败、409/412 或交付失败都不得标记为完成。
106
120
 
107
121
  ## 409 / 412 冲突
108
122
 
@@ -119,7 +133,7 @@ base 已变化时必须拒绝覆盖。
119
133
  - `base` 是 checkout 时的内容;`local` 是发生冲突时的本地快照;`remote` 是重新下载并校验的当前远端内容。
120
134
  - `conflict.json` 记录三份路径、版本、ETag 和哈希,不嵌入完整正文。
121
135
  - Agent 或用户在 worktree local 文件中完成合并;不要手写 HTML 自动合并器,也不要改动保存的三份证据。
122
- - 合并完成后运行 `draftgo check`、必要时 `draftgo verify`,再执行 `draftgo conflict resolve <type> <id>`。
136
+ - 合并完成后运行 `draftgo verify`,再执行 `draftgo conflict resolve <type> <id>`。
123
137
  - resolve 校验 worktree 与 remote,采用 remote 版本作为新的 base,但保留合并后的 worktree;随后运行
124
138
  `draftgo diff` 并 `draftgo commit`。
125
139
 
@@ -1,299 +1,201 @@
1
1
  ---
2
-
3
- read_when: 编写、修改、调试或评审自定义服务时;使用服务 SDK、路由、事件、定时任务或服务依赖时
2
+ read_when: 编写、修改、试运行、发布或评审自定义服务时;使用服务 SDK、Route、Event、Scheduled handler 或服务依赖时
4
3
  ---
5
4
 
6
5
  # Go 自定义服务契约
7
6
 
8
- DraftGo 的新自定义服务使用 Go。服务代码是完整的 `package main`,可以使用标准库和 `go.mod` 中声明的第三方库。平台在保存或发布时编译服务、运行 `Register` 并保存触发器清单;执行时在独立子进程中调用选定 handler。
9
-
10
- 平台 SDK 的固定导入路径是 `draftgo/sdk`。它是 DraftGo 构建器注入的本地 module,不从 GitHub 或其他网络仓库下载,也不要在服务的 `go.mod` 中自行添加或 `replace` 此依赖。
11
-
12
- ## 索引
13
-
14
- - [最小服务](#最小服务)、[本地资源](#本地资源)、[触发器](#触发器)
15
- - [平台 SDK](#平台-sdk)、[AI 平台 SDK](#ai-平台-sdk)
16
- - [权限与运行限制](#权限与运行限制)、[实时 API](#实时-api)、[验收清单](#验收清单)
17
-
18
- ## 最小服务
19
-
20
- ```go
21
- package main
22
-
23
- import "draftgo/sdk"
7
+ 自定义服务是完整的 Go `package main`。平台从源码生成并锁定依赖,执行 `Register` 保存触发器清单,并在独立子进程中运行 handler。SDK 固定导入 `draftgo/sdk`,由构建器注入。
24
8
 
25
- func Register(app *sdk.App) {
26
- app.Route("GET", "/health", health)
27
- app.On("order.paid", afterPaid)
28
- app.Schedule("0 9 * * 1-5", weekdayReport)
29
- }
9
+ ## 最短流程
30
10
 
31
- func health(draftgo *sdk.Context) (any, error) {
32
- return draftgo.Respond(map[string]any{"ok": true}, 200, nil), nil
33
- }
11
+ 先通过 MCP resource/API operation 定位或创建服务并取得 ID;operation schema 以实时 describe 为准。完整正文只走两文件 worktree:
34
12
 
35
- func afterPaid(draftgo *sdk.Context) (any, error) {
36
- draftgo.Log.Info("order paid event received")
37
- return nil, nil
38
- }
39
-
40
- func weekdayReport(draftgo *sdk.Context) (any, error) { return nil, nil }
13
+ ```bash
14
+ draftgo checkout custom-services <id...>
15
+ draftgo diff custom-services <id>
16
+ draftgo commit custom-services <id...>
17
+ draftgo validate custom-services <id>
18
+ draftgo test custom-services <id> --source draft --handler route:POST:/path --input request.json
19
+ draftgo publish custom-services <id...>
41
20
  ```
42
21
 
43
- `Register` 必须没有业务副作用。它只注册 handler;网络请求、写数据库、发通知等操作放在 handler 内。
22
+ `commit custom-services` 是一体化交付入口,会依次完成 commit、validate publish,并在成功后启用线上版本。`publish custom-services` 仍保留为兼容入口;`validate` 和 `test` 仍会先 commit 当前 worktree。不同服务可并发,同一服务保持 edit -> diff -> commit 顺序。
44
23
 
45
- ## 资源读写
24
+ `test` 默认执行 cloud draft;`--source published` 直接测试线上版本且不会提交本地改动,`--source auto` 按生命周期选择。草稿 revision 是内容版本,`validated_revision` / `validated_hash` 绑定源码、依赖、SDK 与 Runner 协议;保存会使旧凭证失效。Runner/SDK 变化导致 `validation_stale` 时,publish 只自动重新 validate 一次;revision 冲突直接停止。
46
25
 
47
- 自定义服务是结构化远端资源,不使用 checkout/commit,也没有约定的本地镜像目录。先用
48
- `draftgo_resource_search` 定位服务,再用 `draftgo_api_search` / `draftgo_api_describe` 获取当前管理接口契约,
49
- 通过 `draftgo_api_call` 读取、创建或更新。Agent 可以在普通工作区文件中编写和检查 Go 源码,但不得把该文件
50
- 误当作远端事实来源;写入时按实时契约显式发送代码和元数据。
26
+ ## Worktree
51
27
 
52
- 常见服务载荷字段如下,实际字段以 `draftgo_api_describe` 为准:
28
+ | 文件 | 用法 |
29
+ |---|---|
30
+ | `service.go` | 完整 `package main`;在 `Register` 中声明 handler |
31
+ | `service.json` | 服务元数据;以实时 checkout/API schema 标出的可编辑字段为准 |
53
32
 
54
- ```json
55
- {
56
- "name": "order-service",
57
- "slug": "order-service",
58
- "mode": "mixed",
59
- "code": "package main\n\n// ...\n",
60
- "go_mod": "module example.com/order-service\n\ngo 1.26.0\n\nrequire github.com/google/uuid v1.6.0\n",
61
- "go_sum": ""
62
- }
63
- ```
33
+ 两个文件都必须保留。`service.json` 中的 `schema_version`、`id`、`revision`、`validation_status`、`validated_revision`、`validated_hash` 是服务端状态,不手工伪造;`Register` 是 Route/Event/Scheduled handler 的唯一事实来源,不在元数据中维护触发器或运行模式。
64
34
 
65
- - 新服务使用 `mode=mixed`,允许同一个 `Register` 同时注册 route、event scheduled。旧服务可继续使用单一 `route`、`event` `scheduled` mode。
66
- - `go_mod` 和可选的 `go_sum` 随服务版本保存,并和代码一起通过实时管理 API 提交。
67
- - 新依赖应锁定明确版本。构建错误会在保存/发布时返回,不会替换当前有效清单。
35
+ CLI 只支持当前 schema v2 的 `service.go`/`service.json` worktree。旧四文件或 schema v1 会明确拒绝;删除旧 worktree 后重新执行 checkout 即可。
68
36
 
69
- ## 触发器
37
+ SDK 固定导入 `draftgo/sdk`。第三方依赖在 `service.go` 顶部声明 `//draftgo:require github.com/google/uuid@v1.6.0`;必须固定显式版本,禁止 floating version、本地路径与自定义 replace。CLI 会从随包发布的 SDK 快照生成 `go.mod/go.sum`、`draftgo_local_main.go` 与 `.draftgo-sdk/`,仅供 gopls 和本地 `go build`;这些带托管标记的文件会自动刷新,不能手工维护,也不会上传。服务端仍独立生成并锁定实际运行依赖。
70
38
 
71
- ### Route
39
+ ## 最小服务
72
40
 
73
41
  ```go
42
+ package main
43
+
44
+ import "draftgo/sdk"
45
+
74
46
  func Register(app *sdk.App) {
75
- app.Route("POST", "/orders", createOrder)
47
+ app.Route("GET", "/health", health)
76
48
  }
77
49
 
78
- func createOrder(draftgo *sdk.Context) (any, error) {
79
- body, _ := draftgo.Input["body"].(map[string]any)
80
- return draftgo.Respond(body, 201, nil), nil
50
+ func health(ctx *sdk.Context) (any, error) {
51
+ return ctx.Respond(map[string]any{"ok": true}, 200, nil), nil
81
52
  }
82
53
  ```
83
54
 
84
- `slug=commerce` 时地址为 `POST /api/x/commerce/orders`。Route 精确匹配,不支持 `/orders/{id}` 模板;ID 使用 query 或 body
55
+ `Register` 只注册 handler,不访问网络、数据库或通知系统。平台在 validate 时执行它并自动记录 handler
85
56
 
86
- `draftgo.Input` 的 Route 字段:`method`、`headers`、`body`、`query_params`、`path_params`。当前身份通过 `draftgo.Auth.CurrentUser()` 获取,入站请求头通过 `draftgo.Headers.Get("Authorization")` 等读取。
57
+ ## 触发器
87
58
 
88
- Route 默认以请求调用者身份访问 `draftgo.DB`、`draftgo.Users` 和其他平台能力。服务由管理员创建、拥有 `scripts:*` 管理权限,或在请求中收到 SAT,都不会让普通 SDK 调用自动提升;`draftgo.Auth.RequireAdmin()` 也只检查当前调用者。
59
+ ### Route
89
60
 
90
- 可信服务需要管理员权限时,逐次显式使用 `draftgo.Admin.*`。这不是服务配置项,也不需要 `admin_access` 开关:调用 `Admin` 就是管理员调用声明。运行时为**这一次**平台 SDK 调用注入管理员身份,并把服务、版本、真实调用者、操作、资源和结果写进该次执行的审计日志;SAT、数据库连接和管理员凭据不会暴露给服务代码。
61
+ `app.Route("POST", "/orders", createOrder)``slug=commerce` 时对应 `POST /api/x/commerce/orders`。Route 精确匹配,不支持 `/orders/{id}` 模板;ID 使用 query 或 body。
91
62
 
92
- ```go
93
- func catalog(draftgo *sdk.Context) (any, error) {
94
- // 继承调用者权限
95
- owned, err := draftgo.DB.Query("order", sdk.QueryOptions{})
96
- if err != nil { return nil, err }
97
-
98
- // 显式管理员权限;仅此调用提升
99
- internal, err := draftgo.Admin.DB.Query("internal_catalog", sdk.QueryOptions{})
100
- if err != nil { return nil, err }
101
-
102
- return draftgo.Respond(map[string]any{
103
- "orders": owned.Items,
104
- "catalog": internal.Items, // 生产代码应再按客户端可见字段组装
105
- }, 200, nil)
106
- }
107
- ```
63
+ 输入字段为 `method`、`headers`、`body`、`query_params`、`path_params`。身份由 `ctx.Auth.CurrentUser()` 获取。普通 `ctx.DB`、`ctx.Users`、`ctx.Billing` 等调用继承请求调用者权限;管理员创建服务、持有服务凭据或 `RequireAdmin` 都不会自动提升 SDK 调用。
108
64
 
109
- `draftgo.Admin` 提供与普通 SDK 对齐的 `DB`、`Users`、`Auth`、`Notify`、`HTTP`、`Cache`、`Config`、`AIHub`、`Knowledge`、`Memory` 能力。它等价于管理员在平台拥有的权限,不做资源级白名单;因此只能授予可信服务编辑者,并且 Route 返回值仍必须由代码负责脱敏。
65
+ `ctx.Admin.*` 是可信服务显式选择的单次管理提升:保持服务主体与已验证执行上下文,但可执行平台级或跨用户/空间管理操作;每次调用都会进入审计,提升不会泄漏到后续普通调用。服务代码仍拿不到服务凭据、数据库连接或管理员凭据。公开 Route 不应无条件调用 Admin;先完成业务鉴权、参数校验和幂等设计,返回值仍需自行脱敏。
110
66
 
111
67
  ### Event
112
68
 
113
- ```go
114
- app.On("user.registered", welcome)
115
-
116
- func welcome(draftgo *sdk.Context) (any, error) {
117
- payload, _ := draftgo.Input["payload"].(map[string]any)
118
- draftgo.Log.Info("registered user: " + fmt.Sprint(payload["user_id"]))
119
- return nil, nil
120
- }
121
- ```
122
-
123
- 事件异步且不阻塞原请求。事件输入含 `event`、`timestamp`、`payload`。
124
-
125
- 事件 `payload` 含 `user_id` 或 `actor_user_id` 且该用户仍存在时,handler 继承该用户身份;无法解析用户时才以系统身份执行。事件服务应把事件数据视为业务输入,而不是把它当成绕过资源权限的通道。
69
+ `app.On("user.registered", welcome)` 异步执行,不阻塞原请求。输入含 `event`、`timestamp`、`payload`。payload 中可解析的 `user_id` / `actor_user_id` 会作为继承身份;无法解析时才使用系统身份。
126
70
 
127
71
  ### Scheduled
128
72
 
129
- ```go
130
- app.Schedule("0 2 * * *", cleanup)
131
- ```
132
-
133
- cron 使用五字段表达式,也支持 `interval:5m`。定时 handler 的 `draftgo.Input` 为空对象。
134
-
135
- 定时任务没有调用者,会以系统身份执行。因此它只能由可信编辑者维护,写入范围应限制在明确的数据类型,并在 handler 中记录可审计的业务日志。
136
-
137
- ## 平台 SDK
138
-
139
- 所有资源访问经受控 RPC 返回 Go 主服务。不要自行读取数据库连接、服务 token 或宿主机环境变量。
140
-
141
- ```go
142
- record, err := draftgo.DB.Create("order", map[string]any{"title": "DraftGo"})
143
- records, err := draftgo.DB.Query("order", sdk.QueryOptions{
144
- Filters: map[string]any{"status": "paid"},
145
- Page: 1, PageSize: 20, OrderBy: "id", Order: "desc",
146
- })
147
-
148
- user, err := draftgo.Users.Get(12)
149
- err = draftgo.Auth.RequireLogin()
150
- err = draftgo.Notify.Send(12, "完成", "订单已创建", "info")
73
+ `app.Schedule("0 2 * * *", cleanup)` 使用五字段 cron,也支持 `interval:5m`。定时任务没有用户调用者;执行空间来自服务持久化的 ResourceOwnership,服务 principal 还必须有覆盖目标资源的 AccessGrant。运行时不会猜测用户或回退到 platform。
151
74
 
152
- cached, err := draftgo.Cache.Get("daily-report")
153
- err = draftgo.Cache.Set("daily-report", map[string]any{"ok": true}, time.Hour)
75
+ ## 试运行
154
76
 
155
- value, err := draftgo.Config.Get("feature_flag", false)
156
- response, err := draftgo.HTTP.Get(draftgo.Context(), "https://api.example.com/health", nil, 10*time.Second)
77
+ ```bash
78
+ # Route;request.json handler input object
79
+ draftgo test custom-services 12 --source draft --handler route:POST:/orders --input request.json
157
80
 
158
- reply, err := draftgo.AIHub.Chat(draftgo.Context(), sdk.AIChatRequest{AgentID: 12, Message: "总结订单"})
159
- draftgo.Log.Info("service completed")
81
+ # Event / Scheduled / 直接 handler
82
+ draftgo test custom-services 12 --handler event:order.paid --input event.json
83
+ draftgo test custom-services 12 --handler scheduled:weekday-report
84
+ draftgo test custom-services 12 --handler health
160
85
  ```
161
86
 
162
- `draftgo.DB` 支持 `Create`、`CreateMany`、`Get`、`Update`、`UpdateMany`、`Delete`、`Query`。`Query` 返回 `sdk.QueryResult{Items, Total, Page, PageSize}`。
163
-
164
- `draftgo.Users` 支持 `Get`、`List`、`Update`。`draftgo.Auth` 支持 `RequireLogin`、`RequireAdmin`、`RequireRole`、`CurrentUser`。
87
+ `--source draft` 是默认值,会提交并验证当前工作树草稿;`--source published` 直接试运行已发布版本,不提交本地改动;`auto` 由服务生命周期决定。`--headers headers.json` 与 `--user user.json` 注入测试上下文,文件必须是 UTF-8 JSON object。默认 `--side-effect-policy deny`:外部 HTTP、通知和 AI 调用会失败;用 `mock` 返回模拟结果。只有用户明确要求真实副作用时才使用 `--side-effect-policy live --test-write`。数据库等 SDK 写操作同样必须显式 `--test-write`。试运行结果包含 execution ID、来源 revision、状态、输出、HTTP 状态/响应头、日志和 SDK operation 摘要。
165
88
 
166
- `draftgo.HTTP` 支持 `Get`、`Post`、`Put`、`Patch`、`Delete`;响应为 `sdk.HTTPResponse{StatusCode, Headers, Data}`。服务可使用 `config.http_allowed_hosts` 限制出站目标;HTTP timeout 最终限制为 1-30 秒,响应体最大 5 MB。
89
+ ## SDK
167
90
 
168
- ## AI 平台 SDK
91
+ 所有平台访问通过受控 RPC 返回主服务,不自行读取宿主环境变量、数据库连接或 token。
169
92
 
170
- | 入口 | 能力 |
93
+ | 入口 | 核心能力 |
171
94
  |---|---|
172
- | `draftgo.AIHub` | Agent/模型推理、图片、Embedding、Agent/Prompt/Skill/MCP 资产 CRUD、供应商、模型路由、Skill 安装与版本、运行记录 |
173
- | `draftgo.Knowledge` | 知识库、文档、Chunk CRUD,文档上传、检索、重建索引 |
174
- | `draftgo.Memory` | 长期记忆 CRUD 与全局检索配置 |
175
-
176
- 普通入口继承调用用户身份,继续经过 `aihub:read/create/update/delete/execute/invoke` 等平台权限检查。`draftgo.Admin.AIHub`、`draftgo.Admin.Knowledge`、`draftgo.Admin.Memory` 提供同构接口,每次调用以管理员身份执行并进入自定义服务执行审计。
177
-
178
- ### 推理与配置
95
+ | `ctx.DB` | Create/CreateMany/Get/Update/UpdateMany/Delete/Query |
96
+ | `ctx.Users` | Get/List/Update |
97
+ | `ctx.Auth` | RequireLogin/RequireAdmin/RequireRole/CurrentUser |
98
+ | `ctx.Notify` | 站内通知 |
99
+ | `ctx.HTTP` | Get/Post/Put/Patch/Delete |
100
+ | `ctx.Cache` / `Config` | 缓存与系统配置 |
101
+ | `ctx.AIHub` | Agent/模型推理、图片、Embedding、AI 资产与运行记录 |
102
+ | `ctx.Knowledge` | 知识库、文档、Chunk、上传、检索和重建 |
103
+ | `ctx.Memory` | 长期记忆与全局检索配置 |
104
+ | `ctx.Files` | 文件夹、资产上传/下载、绑定、回收站和恢复 |
105
+ | `ctx.Scope` | 当前 platform/space、principal 与 ResourceOwnership 授权检查 |
106
+ | `ctx.Billing` | 账本、权益、支付、套餐、订阅和 AI 计费;所有写操作按实时权限与风险契约执行 |
107
+
108
+ 常用示例:
179
109
 
180
110
  ```go
181
- reply, err := draftgo.AIHub.Chat(draftgo.Context(), sdk.AIChatRequest{
182
- AgentID: 12,
183
- Message: "总结订单",
184
- SessionID: "optional-conversation-id",
185
- RunID: "optional-client-run-id",
111
+ records, err := ctx.DB.Query("order", sdk.QueryOptions{
112
+ Filters: map[string]any{"status": "paid"}, Page: 1, PageSize: 20,
186
113
  })
187
-
188
- direct, err := draftgo.AIHub.Chat(draftgo.Context(), sdk.AIChatRequest{
189
- Model: "gpt-4.1-mini",
190
- Messages: []map[string]any{{"role": "user", "content": "hello"}},
191
- })
192
-
193
- vectors, err := draftgo.AIHub.Embeddings(draftgo.Context(), sdk.AIEmbeddingRequest{
194
- Model: "text-embedding-3-small",
195
- Input: []string{"first document", "second document"},
114
+ reply, err := ctx.AIHub.Chat(ctx.Context(), sdk.AIChatRequest{
115
+ AgentID: 12, Message: "总结订单",
196
116
  })
117
+ response, err := ctx.HTTP.Get(ctx.Context(), "https://api.example.com/health", nil, 10*time.Second)
197
118
  ```
198
119
 
199
- `GenerateImage` 支持 Agent 和直接模型两种调用。普通入口的推理必须在服务 `config.aihub` 中显式开启:
200
-
201
- ```json
202
- {
203
- "aihub": {
204
- "enabled": true,
205
- "allowed_agents": [12, 18],
206
- "allow_direct_model_call": true,
207
- "allowed_models": ["gpt-4.1-mini", "text-embedding-3-small"],
208
- "allow_images": true
209
- }
210
- }
211
- ```
120
+ 精确签名以 checkout 后自动生成的 `.draftgo-sdk/billing.go` 为准;该文件由 CLI 同步当前 SDK,只读不改。不要猜方法名、字段或直接调用账务 HTTP API。
212
121
 
213
- 空白名单表示不额外限制,但用户权限、Agent 调用权限和模型外部调用策略仍然生效。Agent 预览与后续新增的 `/api/v1/*` 推理端点也使用同一组白名单策略。
122
+ ### Billing 决策与用法
214
123
 
215
- ### AI 资产、Skill MCP
124
+ 需要余额、退款、权益、套餐或订阅时使用 `ctx.Billing` / `ctx.Admin.Billing`,不得用动态 DB、缓存或自行维护余额重建金钱状态。金额统一使用最小货币单位:人民币 `AmountMinor: 990` 表示 9.90 元。
216
125
 
217
- Agent、Prompt、Skill、MCP 使用统一资产 CRUD,通过 `type` 区分:
126
+ | 场景 | 方法 |
127
+ |---|---|
128
+ | 当前调用者操作服务归属内账务 | `ctx.Billing.*` |
129
+ | 可信服务执行需要管理员权限的账务管理 | `ctx.Admin.Billing.*`;目标仍由服务端资源归属决定 |
130
+ | 金额已确定且应立即扣除 | `DebitAccount` |
131
+ | 最终金额不确定或业务可能失败 | `HoldFunds` -> `SettleHold`;失败时 `ReleaseHold` |
132
+ | 更正已入账流水 | `ReverseJournal`,不要用反向充值伪造冲正 |
133
+ | 次数、额度或功能许可 | `ConsumeEntitlement`,不要混用钱包余额 |
134
+ | 三方支付退款 | `RefundPaymentOrder` |
218
135
 
219
- ```go
220
- skills, err := draftgo.AIHub.ListAssets(draftgo.Context(), map[string]any{
221
- "type": "skill", "page": 1, "page_size": 50,
222
- })
136
+ 直接扣款示例:
223
137
 
224
- mcp, err := draftgo.AIHub.CreateAsset(draftgo.Context(), map[string]any{
225
- "type": "mcp", "name": "internal-tools",
226
- "data": map[string]any{"transport": "http", "url": "https://mcp.example.com"},
227
- "status": 1,
228
- })
229
-
230
- tools, err := draftgo.AIHub.DiscoverMCPTools(draftgo.Context(), map[string]any{
231
- "transport": "http", "url": "https://mcp.example.com",
138
+ ```go
139
+ result, err := ctx.Billing.DebitAccount(ctx.Context(), sdk.MoneyOperationRequest{
140
+ AmountMinor: 990,
141
+ Reference: "order:20260829-001",
142
+ Reason: "购买专业版功能",
143
+ IdempotencyKey: "order:20260829-001:debit",
232
144
  })
145
+ if err != nil {
146
+ return ctx.Respond(map[string]any{"error": "扣款失败"}, 409, nil), nil
147
+ }
148
+ return ctx.Respond(map[string]any{
149
+ "journal_id": result.JournalID,
150
+ "balance_minor": result.BalanceMinor,
151
+ }, 200, nil), nil
233
152
  ```
234
153
 
235
- 资产方法包括 `ListAssets/GetAsset/CreateAsset/UpdateAsset/UpdateAssets/DeleteAsset`、`AgentReadiness/PublishAgent`、`DiscoverMCPTools`、`InstallSkill/InstallSkillArchive/ListSkillVersions/RollbackSkill`。供应商使用 `ListProviders/GetProvider/CreateProvider/UpdateProvider/DeleteProvider/DiscoverProvider/SyncProvider`;模型和路由使用 `ListModels/GetModel/CreateModel/UpdateModel/DeleteModel/ProbeModel/UpdateModelRoute/DeleteModelRoute`;运行记录使用 `ListRuns/GetRun/DeleteRuns`。
154
+ 每个金额写操作都必须使用可持久复用、业务唯一的 `IdempotencyKey`,推荐 `{业务类型}:{业务ID}:{动作}`;不得使用时间戳或每次重试生成的新 UUID。相同 key 的相同请求安全重试;同一 key 改变目标或金额会发生幂等冲突。余额不足时操作失败且不会产生负余额。
236
155
 
237
- ### 知识库与记忆
156
+ 预授权流程必须为 `HoldFunds` -> `SettleHold` / `ReleaseHold`,三个动作分别使用稳定幂等键,并持久化返回的 `HoldID`。不确定远端调用是否已成功时,先按业务 ID 或流水回读,不自动换 key 重扣。日志只能记录业务引用、`JournalID` / `HoldID` 和脱敏错误,不记录完整用户资料或支付凭据。
238
157
 
239
- ```go
240
- base, err := draftgo.Knowledge.Create(draftgo.Context(), map[string]any{
241
- "name": "产品手册", "embedding_model_id": 7,
242
- })
243
-
244
- document, err := draftgo.Knowledge.UploadDocument(draftgo.Context(), baseID,
245
- sdk.KnowledgeDocumentUpload{
246
- Filename: "manual.pdf", MIMEType: "application/pdf", Content: pdfBytes,
247
- Metadata: map[string]any{"product": "DraftGo"},
248
- })
249
-
250
- matches, err := draftgo.Knowledge.Retrieve(draftgo.Context(), baseID, map[string]any{
251
- "query": "如何配置模型供应商?", "mode": "hybrid", "top_k": 6,
252
- })
158
+ 试运行真实账务写入必须显式执行:
253
159
 
254
- memory, err := draftgo.Memory.Create(draftgo.Context(), map[string]any{
255
- "scope": "user_agent", "user_id": 42, "agent_id": 12,
256
- "content": "用户偏好简洁的中文回答", "importance": 0.8,
257
- })
160
+ ```bash
161
+ draftgo test custom-services 12 --source draft --handler route:POST:/charge --input request.json --side-effect-policy live --test-write
258
162
  ```
259
163
 
260
- `Knowledge` 还提供知识库 `List/Get/Update/Delete`、文档 `ListDocuments/GetDocument/UpdateDocument/DeleteDocument/ReindexDocument`、Chunk `ListChunks/UpdateChunk` 以及 `RebuildIndex`。文档上传受 8 MiB 限制。`Memory` 提供 `List/Get/Create/Update/Delete`,全局配置使用 `draftgo.Memory.GetConfig` 和 `draftgo.Memory.UpdateConfig`。
164
+ 只验证编译与路由时不要开启真实写入。涉及金额的测试使用专门测试账户、最小金额和唯一业务 ID,完成后回读余额与流水;禁止在生产用户账户上试扣。
261
165
 
262
- ### 扩展入口
166
+ HTTP host 可由 `config.http_allowed_hosts` 限制;timeout 最终为 1-30 秒,响应体最大 5 MB。日志每次执行最多 500 条、单条 4096 字符,不记录 token、Cookie、密码或完整个人信息。
263
167
 
264
- typed helper 尚未覆盖新端点时,使用 `AIHub.Request`:
168
+ ### AI 权限
265
169
 
266
- ```go
267
- value, err := draftgo.AIHub.Request(draftgo.Context(), sdk.AIRequest{
268
- Method: "GET",
269
- Path: "/api/aihub/types",
270
- })
271
- ```
170
+ 普通 AIHub、Knowledge、Memory 使用调用者与服务 Grant 的权限交集。AIHub 使用 `aihub:*`,Knowledge 独立使用 `knowledge:*`;两组权限互不包含。显式 `ctx.Admin.AIHub`、`ctx.Admin.Knowledge`、`ctx.Admin.Memory` 对单次可信服务调用启用管理提升并写入审计。
272
171
 
273
- `Request` 只接受 `/api/aihub`、`/api/agents`、`/api/v1`、`/api/images`、`/api/knowledge-bases`、`/api/memories`、`/api/skills` 路径;禁止外部 URL、路径穿越和在 `Path` 中拼 query。身份、权限、推理开关与审计规则和 typed helper 相同。
172
+ 服务不再配置 AI 启用开关、图片开关、直接模型调用开关、Agent 白名单或模型白名单;`config.aihub` 会被拒绝。Agent 与模型是否可调用直接跟随 AI 板块自身配置。普通调用按调用者与服务 Grant 的交集鉴权;只有源码中显式选择的 `ctx.Admin.*` 才启用单次可信管理提升。
274
173
 
275
- 日志每次执行最多 500 条、单条最多 4096 字符。不要记录 token、Cookie、密码或完整个人信息。
174
+ typed helper 未覆盖的新端点可使用 `ctx.AIHub.Request`,但 path 只能位于平台允许的 AI/Knowledge/Memory 前缀,不能传外部 URL、路径穿越或内嵌 query。
276
175
 
277
- ## 权限与运行限制
176
+ ## 权限与运行
278
177
 
279
- - `scripts:read/create/update/delete/execute` 控制可信人员管理服务。
280
- - Route 调用者仍由服务 `permission` `config.route_security` 控制;管理权限不绕过 Route 调用权限。
281
- - Route 的普通 SDK 数据访问继承调用者权限;不要把“管理员创建服务”误写成自动提升。只有显式 `draftgo.Admin.*` 调用才以管理员执行并记录审计;定时任务和无可解析用户的事件是系统身份例外。
282
- - `config.timeout`、`max_concurrency`、`queue_timeout_ms` 适用于服务执行。Route 饱和时返回 HTTP 429。
283
- - Go 服务以独立进程运行,超时会终止该进程;它不是为不可信多租户代码准备的安全沙箱。只向可信编辑者授予服务编辑权限。
284
- - 每个保存版本按源码、依赖、SDK 和 Runner 协议生成不可变构建键。代码或依赖变更会生成新构建产物;旧版本可通过现有版本恢复接口重新激活。
178
+ - `scripts:read/create/update/delete/execute` 控制服务管理。
179
+ - Role 不带作用域;服务必须通过覆盖持久化 ResourceOwnership 的有效 AccessGrant 授权。工作区成员关系不能单独授权。
180
+ - platform Grant 可跨空间但只能使用显式权限;space Grant 不能跨根。请求中的范围不能覆盖服务已保存的归属。
181
+ - 普通服务调用访问动态 DB 时仍受目标 DB 的 DataRange(`none` / `own` / `all`)限制;`ctx.Admin.*` 只应用于源码明确选择的单次可信管理操作,不能从请求参数隐式开启。
182
+ - Route 仍受服务 `permission` 与 `config.route_security` 控制。
183
+ - 定时任务和无可解析用户的事件默认使用服务主体及其持久化空间;需要平台级或跨归属管理时必须在对应单次调用显式使用 `ctx.Admin.*`。
184
+ - 服务 principal 由平台运行时注入;不要在 `service.json`、源码、测试参数或日志中保存/模拟服务身份凭据。Route/Event 取调用者与服务 Grant 的权限交集,Scheduled 使用服务持久化 ownership,而不是假造用户 API Key。
185
+ - `config.timeout`、`max_concurrency`、`queue_timeout_ms` 控制执行;Route 饱和返回 429。
186
+ - 超时会终止独立子进程。运行器不是不可信多租户安全沙箱,只授予可信编辑者服务权限。
187
+ - 构建键覆盖源码、依赖、SDK 和 Runner 协议;有效验证凭证绑定该键,任一部分变化都必须重新验证。
285
188
 
286
189
  ## 实时 API
287
190
 
288
- 服务管理接口使用标准 `{code, data, message}` 信封并受 `scripts:*` 权限控制。具体 operation、参数、请求体、响应和风险必须通过 MCP `draftgo_api_search` `draftgo_api_describe` 获取后再调用,不维护静态路径表。
191
+ 管理接口使用标准 `{code,data,message}` 信封。未知 operation search,首次使用或 registry revision 变化时 describe;完整服务正文不进入 MCP 参数。
289
192
 
290
- 有效 `app.Route` 会以实际 method、`/api/x/{slug}/{path}`、权限、风险和输入 schema 动态加入同一 MCP registry;搜索 `resource_type=custom_scripts` 或 `module=scripts_dynamic` 后描述并调用。运行时 Route 返回 handler 自定义响应,不强制使用管理 API 信封;精确匹配、调用身份和运行限制仍以本文件的 Route 与权限章节为准。
193
+ 有效 `app.Route` 会按实际 method 和 `/api/x/{slug}/{path}` 动态加入 MCP registry。运行时响应由 handler 决定,不强制管理 API 信封;精确匹配、身份继承和执行限制仍以本文件为准。
291
194
 
292
- ## 验收清单
195
+ ## 完成条件
293
196
 
294
- - [ ] `package main` 且实现 `Register(app *sdk.App)`。
295
- - [ ] 服务使用 `mode=mixed`,第三方库写入 `go_mod`。
296
- - [ ] 路由使用 `app.Route`,事件使用 `app.On`,定时任务使用 `app.Schedule`。
297
- - [ ] handler 返回 `(any, error)`,需要状态码时使用 `draftgo.Respond`。
298
- - [ ] Route 显式设置 `permission` 与 `route_security`。
299
- - [ ] 保存或发布后请求无副作用 GET Route,并记录状态码和响应作为验收证据。
197
+ - `package main` 且实现无业务副作用的 `Register(app *sdk.App)`。
198
+ - `service.go`、`service.json` 齐全,handler 仅在 `Register` 注册,服务端状态字段未被手工伪造。
199
+ - trigger handler 签名正确,第三方依赖已锁定。
200
+ - Route 权限、管理员调用和返回脱敏符合真实调用者范围。
201
+ - `draftgo commit custom-services` 成功并显示 published;需要运行行为证据时执行相关 `draftgo test`。
@@ -4,6 +4,21 @@ read_when: 操作动态 DB 之前 · 设计数据结构时 · 使用 filters 检
4
4
 
5
5
  # 动态 DB & 数据层
6
6
 
7
+ ## 最短管理流程
8
+
9
+ 结构和记录都走实时 API 契约,不 checkout:
10
+
11
+ ```bash
12
+ draftgo api search "db meta"
13
+ draftgo api describe <operation_id>
14
+ draftgo api call <operation_id> --input request.json --output json
15
+ draftgo api search "dynamic db record"
16
+ ```
17
+
18
+ 创建或修改 schema 后,先回读目标 `type`,再调用记录 operation 验证最小 CRUD;不要把管理端 `db_meta` operation 与页面运行时的 `App.get/post/put/delete('db/...')` 混用。失败时优先核对 describe 的字段容器、`searchable`、权限、记录 owner 和分页,而不是猜静态路径。
19
+
20
+ 完成条件:schema 可按 `type` 回读,目标角色的最小 CRUD 与筛选行为通过;批量写入还要验证失败时整批回滚。
21
+
7
22
  ## DB Meta 结构
8
23
 
9
24
  ```json
@@ -46,12 +61,12 @@ read_when: 操作动态 DB 之前 · 设计数据结构时 · 使用 filters 检
46
61
 
47
62
  ---
48
63
 
49
- ## 自定义服务内的 draftgo.DB.Query
64
+ ## 自定义服务内的 ctx.DB.Query
50
65
 
51
- Go 自定义服务使用 `draftgo.DB.Query(type, sdk.QueryOptions{...})` 操作动态 DB,返回 `sdk.QueryResult`:
66
+ Go 自定义服务使用 `ctx.DB.Query(type, sdk.QueryOptions{...})` 操作动态 DB,返回 `sdk.QueryResult`:
52
67
 
53
68
  ```go
54
- result, err := draftgo.DB.Query("order", sdk.QueryOptions{
69
+ result, err := ctx.DB.Query("order", sdk.QueryOptions{
55
70
  Filters: map[string]any{"status": "paid"},
56
71
  Page: 1, PageSize: 20, OrderBy: "id", Order: "desc",
57
72
  })
@@ -71,7 +86,7 @@ items := result.Items
71
86
  `filters` 操作符示例:
72
87
 
73
88
  ```go
74
- draftgo.DB.Query("order", sdk.QueryOptions{Filters: map[string]any{
89
+ ctx.DB.Query("order", sdk.QueryOptions{Filters: map[string]any{
75
90
  "status": "paid",
76
91
  "customer_name": map[string]any{"op": "like", "value": "张"},
77
92
  "amount": map[string]any{"op": "gte", "value": 100},
@@ -152,8 +167,9 @@ await App.patch(`db/order/batch`, [
152
167
 
153
168
  ## db_meta 实时契约
154
169
 
155
- db_meta 是结构化远端资源,不 checkout,也不生成本地 `index.json`。开发前先通过 MCP
156
- `draftgo_api_search` / `draftgo_api_describe` 确认接口,再用 `draftgo_api_call` 查询目标 type 和 schema。
170
+ db_meta 是结构化远端资源,不 checkout,也不生成本地索引。未知 operation 才通过 MCP
171
+ `draftgo_api_search` 定位;首次使用或 registry revision 变化时 `draftgo_api_describe`,再用
172
+ `draftgo_api_call` 查询目标 type 和 schema。
157
173
  典型返回条目如下:
158
174
 
159
175
  ```json
@@ -0,0 +1,33 @@
1
+ ---
2
+ read_when: 准备验证、提交、发布、交付或标记工作项完成时
3
+ ---
4
+
5
+ # 交付验收
6
+
7
+ 先按资源类型选择唯一交付链,不把“本地验证通过”写成“已发布”。
8
+
9
+ ## 长正文
10
+
11
+ ```bash
12
+ draftgo diff pages 42 --stat
13
+ draftgo verify pages 42
14
+ draftgo commit pages 42
15
+ draftgo check --remote --output json
16
+ ```
17
+
18
+ `nav`、`docs` 同理。先用 `--stat` 或 `--summary` 确认变更范围,只有需要审查正文时才展开完整 diff。`commit` 返回新版本与哈希后,再用远端检查确认基线一致。409/412、未解决冲突或远端状态不明时停止。
19
+
20
+ ## 结构化资源与自定义服务
21
+
22
+ 结构化资源按最新 describe 调用写 operation,再用 get/list operation 回读。自定义服务执行 `diff -> commit`,其中 commit 自动完成 validate 和 publish;需要运行行为证据时再执行 test。
23
+
24
+ ## 统一收尾
25
+
26
+ ```bash
27
+ draftgo verify
28
+ draftgo work complete <ref> --note "<结果与证据>"
29
+ ```
30
+
31
+ 默认 `verify` 不启动浏览器。只有用户明确要求视觉验收时,才增加 `--url <url> --screenshot always`;需要 DOM 或交互验证时再用 `--ui always`。所有 commit、MCP 写入、必要 test/publish 和回读都成功后才能 complete;失败项保持 active,并在 note 外记录可脱敏的 request ID。
32
+
33
+ 完成条件:本地检查通过,目标远端资源可回读,版本/哈希或结构化状态与预期一致,任务要求的运行或视觉证据齐全,且证据不含凭据。