kld-sdd 2.6.16 → 2.6.17

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 (58) hide show
  1. package/lib/init.js +65 -6
  2. package/lib/skills-bundle.js +20 -1
  3. package/lib/tool-profiles.js +8 -0
  4. package/package.json +5 -1
  5. package/skywalk-sdd/apply-worktree-finish.cjs +2 -23
  6. package/skywalk-sdd/context-client.cjs +38 -87
  7. package/skywalk-sdd/index.cjs +719 -108
  8. package/skywalk-sdd/kb-sync-identity.cjs +780 -0
  9. package/skywalk-sdd/kb-upload.cjs +505 -0
  10. package/skywalk-sdd/lib/shared.cjs +663 -0
  11. package/skywalk-sdd/metrics-v3.cjs +138 -8
  12. package/skywalk-sdd/ontology/archive-package.cjs +19 -34
  13. package/skywalk-sdd/ontology/change-lock.cjs +3 -7
  14. package/skywalk-sdd/ontology/external-key.cjs +18 -4
  15. package/skywalk-sdd/ontology/id.cjs +26 -5
  16. package/skywalk-sdd/ontology/identity-index.cjs +3 -7
  17. package/skywalk-sdd/ontology/resolve-spec-root.cjs +20 -6
  18. package/skywalk-sdd/ontology/runtime.cjs +16 -12
  19. package/skywalk-sdd/ontology/traceability-validator.cjs +7 -4
  20. package/skywalk-sdd/reporting/change-report-markdown.cjs +137 -19
  21. package/skywalk-sdd/reporting/change-report-model.cjs +993 -14
  22. package/skywalk-sdd/reporting/change-report-renderer.cjs +106 -41
  23. package/skywalk-sdd/reporting/change-report-view-model.cjs +272 -43
  24. package/skywalk-sdd/reporting/core-metric-definitions.cjs +192 -0
  25. package/skywalk-sdd/spec-root.cjs +31 -0
  26. package/templates/hooks/codebuddy/hooks/hook-gate-core.cjs +327 -0
  27. package/templates/hooks/codebuddy/hooks/sdd-apply-test-gate.cjs +54 -9
  28. package/templates/hooks/codebuddy/hooks/sdd-mid-checkpoint.cjs +63 -6
  29. package/templates/hooks/codebuddy/hooks/sdd-tdd-rhythm-gate.cjs +113 -65
  30. package/templates/openspec/proposal.md +7 -3
  31. package/templates/openspec/spec.md +3 -3
  32. package/templates/skills/kld-sdd/opsx-apply/SKILL.md +8 -6
  33. package/templates/skills/kld-sdd/opsx-apply/checklist.md +2 -0
  34. package/templates/skills/kld-sdd/opsx-apply/reference.md +29 -7
  35. package/templates/skills/kld-sdd/opsx-archive/SKILL.md +6 -5
  36. package/templates/skills/kld-sdd/opsx-check/SKILL.md +48 -16
  37. package/templates/skills/kld-sdd/opsx-check/checklist.md +4 -2
  38. package/templates/skills/kld-sdd/opsx-design/SKILL.md +2 -2
  39. package/templates/skills/kld-sdd/opsx-explore/SKILL.md +2 -2
  40. package/templates/skills/kld-sdd/opsx-kb-config/SKILL.md +164 -0
  41. package/templates/skills/kld-sdd/opsx-kb-config/reference.md +116 -0
  42. package/templates/skills/kld-sdd/opsx-kb-ingest/SKILL.md +218 -53
  43. package/templates/skills/kld-sdd/opsx-kb-ingest/reference.md +51 -9
  44. package/templates/skills/kld-sdd/opsx-ontology-query/SKILL.md +12 -50
  45. package/templates/skills/kld-sdd/opsx-ontology-query/phase-3-postchange.md +2 -2
  46. package/templates/skills/kld-sdd/opsx-ontology-query/reference.md +1 -1
  47. package/templates/skills/kld-sdd/opsx-propose/SKILL.md +35 -23
  48. package/templates/skills/kld-sdd/opsx-propose/checklist.md +2 -0
  49. package/templates/skills/kld-sdd/opsx-propose/reference.md +22 -17
  50. package/templates/skills/kld-sdd/opsx-rules/SKILL.md +2 -2
  51. package/templates/skills/kld-sdd/opsx-spec/SKILL.md +19 -15
  52. package/templates/skills/kld-sdd/opsx-spec/checklist.md +2 -0
  53. package/templates/skills/kld-sdd/opsx-task/SKILL.md +2 -4
  54. package/templates/skills/kld-sdd/opsx-test/SKILL.md +2 -2
  55. package/templates/skills/kld-sdd/tdd-core/reference.md +1 -1
  56. package/templates/skills/kld-sdd/tdd-rules/rules/test-skeleton-telemetry.md +1 -1
  57. package/templates/skills/kld-sdd/opsx-kb-ingest/state.example.json +0 -7
  58. package/templates/skills/kld-sdd/opsx-ontology-query/state.example.json +0 -7
@@ -47,8 +47,8 @@ allowed-tools:
47
47
 
48
48
  > **🖥️ 跨平台执行规则**
49
49
  > - **SDD 文档根** = `*-sdd-specs` 包裹包(含 `openspec/`、`modules.yaml`),不是 Git 根或工作区根。
50
- > - `openspec` 命令:优先 `node "$(cat .sdd-spec-root)/skywalk-sdd/openspec-shim.cjs" list`(自动 cd 到包裹包),或先 `cd` 到包裹包再执行。
51
- > - Telemetry / ontology:`node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" …`(Git 根无 skywalk-sdd/);`spec-root` 可用 `node "$(cat .sdd-spec-root)/skywalk-sdd/ontology/cli.cjs" spec-root`。
50
+ > - `openspec` 命令:`cd <spec-package> && openspec …`(先 cd 到包裹包即可)。路径不确定时用 `node <spec-package>/skywalk-sdd/spec-root.cjs` 验证。
51
+ > - Telemetry / ontology:`node <spec-package>/skywalk-sdd/log.cjs …`(直接在包裹包内执行);`--project=.` 指当前 spec 包裹包。
52
52
  > - Telemetry 命令默认使用 `--project=.`,兼容 Windows、macOS、Linux。
53
53
  > - ${SHELL_GUIDANCE}
54
54
  > - 不要省略 `--source=opsx-command` 与 `--session-id=<会话ID>`。
@@ -17,8 +17,8 @@ allowed-tools:
17
17
 
18
18
  > **🖥️ 跨平台执行规则**
19
19
  > - **SDD 文档根** = `*-sdd-specs` 包裹包(含 `openspec/`、`modules.yaml`),不是 Git 根或工作区根。
20
- > - `openspec` 命令:优先 `node "$(cat .sdd-spec-root)/skywalk-sdd/openspec-shim.cjs" list`(自动 cd 到包裹包),或先 `cd` 到包裹包再执行。
21
- > - Telemetry / ontology:`node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" …`(Git 根无 skywalk-sdd/);`spec-root` 可用 `node "$(cat .sdd-spec-root)/skywalk-sdd/ontology/cli.cjs" spec-root`。
20
+ > - `openspec` 命令:`cd <spec-package> && openspec …`(先 cd 到包裹包即可)。路径不确定时用 `node <spec-package>/skywalk-sdd/spec-root.cjs` 验证。
21
+ > - Telemetry / ontology:`node <spec-package>/skywalk-sdd/log.cjs …`(直接在包裹包内执行);`--project=.` 指当前 spec 包裹包。
22
22
  > - Telemetry 命令默认使用 `--project=.`,兼容 Windows、macOS、Linux。
23
23
  > - ${SHELL_GUIDANCE}
24
24
  > - 不要省略 `--source=opsx-command` 与 `--session-id=<会话ID>`。
@@ -0,0 +1,164 @@
1
+ ---
2
+ name: opsx-kb-config
3
+ description: >-
4
+ Configures Engineering KB access (API Key + Space/KB selection + project-identity).
5
+ Shared prerequisite for opsx-ontology-query, opsx-kb-ingest, and kb-upload.
6
+ Run once, all KB skills become available. Also supports changing KB spaces.
7
+ license: MIT
8
+ compatibility: Requires Engineering KB API (API Key with context:read + archive:ingest).
9
+ metadata:
10
+ author: sdd-team
11
+ version: "1.0"
12
+ source: "kb-sdd/skills/opsx-kb-config"
13
+ allowed-tools:
14
+ - Bash
15
+ - Read
16
+ - Write
17
+ - Edit
18
+ ---
19
+
20
+ # 本体知识库 · 配置
21
+
22
+ 只负责**配置**。一次配置,查询(opsx-ontology-query)、入库(opsx-kb-ingest)、文件夹上传(kb-upload)全部可用。
23
+
24
+ > **📡 共享状态**:配置写入 `kb-state.json`(位于 spec 仓根目录),所有 KB 技能共用。任一技能配置后,其他技能自动可用,无需重复输入。
25
+
26
+ > **🖥️ 跨平台执行规则**(Windows 用户必读):
27
+ > - KB 配置涉及 HTTP 请求和 JSON 文件读写,**禁止**在 PowerShell 中使用 `curl`(被别名为 Invoke-WebRequest)→ 用 `Invoke-RestMethod` 或 Node.js 脚本
28
+ > - **禁止**用 PowerShell `Get-Content` / `Out-File` 读写 JSON(默认写 BOM 导致 JSON.parse 失败)→ 用 write_to_file 工具或 Node.js `fs` 模块
29
+ > - **禁止** `| cat` 管道(PowerShell 将 cat 解释为 Get-Content 不接受管道输入)→ 直接执行命令
30
+ > - **禁止**用 `node -e "..."` 内联脚本(PowerShell 对 `||`/`{}`/`()` 有特殊解析,导致 JS 语法被破坏)→ 用 `write_to_file` 创建临时 `.cjs` 脚本执行后删除
31
+ > - **⚠️ PowerShell CLIXML**:直接执行 `node xxx.cjs` 时 stdout 可能被包装为 CLIXML `<Objs>` 格式导致 JSON 解析失败。`lib/shared.cjs` 已内置 `stripCliXml()` 剥离函数;临时脚本中可用 `require('./lib/shared.cjs').stripCliXml(output)` 处理
32
+ > - **推荐**用 `write_to_file` 创建临时 `.cjs` 脚本执行所有 Node.js 逻辑,执行完后 `delete_file` 清理
33
+
34
+ ## 能做什么
35
+
36
+ | 场景 | 说明 |
37
+ |------|------|
38
+ | 首次配置 | 输入 API Key → 选择 Space/KB → 创建 project-identity.json |
39
+ | 更换 API Key | 清空 apiKey,重新输入 |
40
+ | 更换知识库 | 清空 targets,重新选择 Space/KB |
41
+ | 查看当前配置 | 展示已配置的 Space/KB 列表 |
42
+ | 校验配置 | 探活 API + 校验 project_id 与 spaceKey 一致 |
43
+
44
+ ## 配置步骤
45
+
46
+ ### 1. 读取共享状态
47
+
48
+ ```bash
49
+ # 读取 spec 包裹包路径
50
+ SPEC_ROOT=$(node <spec-package>/skywalk-sdd/spec-root.cjs)
51
+
52
+ # 读取共享 KB 配置(用 write_to_file 创建临时脚本,勿用 node -e)
53
+ # 临时脚本内容:
54
+ # const fs = require('fs');
55
+ # try { console.log(fs.readFileSync(process.argv[1], 'utf8')); }
56
+ # catch { console.log('{}'); }
57
+ # 执行:node _tmp-read-state.cjs "$SPEC_ROOT/kb-state.json"
58
+ # 或直接用 read_file 工具读取该文件
59
+ ```
60
+
61
+ ### 2. API Key
62
+
63
+ 若 `state.apiKey` 为空或用户要求换密钥:
64
+
65
+ 1. 请用户提供 API Key(控制台「API 密钥」创建,scope 建议同时勾选 `context:read` + `archive:ingest`,使查询和入库都能复用)。
66
+ 2. 可选:请用户确认 `api`(默认 `http://10.29.213.80:8080/api`)与 `tenantKey`(默认 `default`)。
67
+ 3. 写入 `kb-state.json`(spec 仓根目录,已 gitignore)。
68
+ 4. 用 `GET $API/health` 探活;再用 `GET $API/v1/spaces?tenantKey=…` + Bearer 校验 key。
69
+
70
+ **安全规则**:
71
+ - 完整 apiKey 只写共享 state 文件;聊天里最多显示前缀(如 `sk_sdd_****`)。
72
+ - 401/403 → 清掉 apiKey 请用户重贴,勿循环重试。
73
+ - **不要** `POST /auth/login`。
74
+
75
+ ### 3. 选择空间与知识库(支持多选)
76
+
77
+ 若 `state.targets` 为空,或用户要求重新选择:
78
+
79
+ 1. `GET $API/v1/spaces?tenantKey=$TENANT_KEY`
80
+ 2. 对每个相关 space:`GET $API/v1/spaces/{spaceId}/knowledge-bases`
81
+ 3. 向用户展示「空间名 / spaceId → KB 名 / kbId」清单,**允许多选**。
82
+ 4. 写入 `targets: [{ spaceId, spaceName, spaceKey, kbId, kbName }, …]`。
83
+
84
+ > **API 返回字段说明**(临时脚本中注意字段名):
85
+ > - Space 列表返回 `spaceId`(camelCase),不是 `id`。临时脚本中使用 `s.spaceId || s.id` 兼容。
86
+ > - Space 列表返回 `spaceKey`(项目标识,非 UUID),用于 `project-identity.json` 的 `project_id`。
87
+ > - KB 列表返回 `kbId`(camelCase),不是 `id`。
88
+
89
+ ### 4. 写入项目身份文件(首次配置 Space 后必做)
90
+
91
+ > `project-identity.json` 是 spec 仓 Git 中的**团队共享**文件,记录 `project_id`(= KB Space 的 `spaceKey`)。Archive 阶段 `archive-docs` 读取此文件生成 `archive-manifest.json`,kb-ingest / kb-upload 上传时 KB 校验 `project_id === spaceKey`。
92
+
93
+ 选择 Space 完成后(Step 3 写入 `targets` 后),执行以下逻辑:
94
+
95
+ 1. 读取 spec 包裹包路径:
96
+ ```bash
97
+ SPEC_ROOT=$(node <spec-package>/skywalk-sdd/spec-root.cjs)
98
+ ```
99
+ 2. 检查 `$SPEC_ROOT/skywalk-sdd/project-identity.json` 是否已存在
100
+ 3. **不存在** → 创建(取 `targets[0].spaceKey` 作为 `project_id`):
101
+ ```json
102
+ {
103
+ "schema_version": "kld-sdd-project-identity/v1",
104
+ "project_id": "<targets[0].spaceKey>",
105
+ "created_at": "<ISO timestamp>",
106
+ "kb_space_id": "<targets[0].spaceId>",
107
+ "kb_space_name": "<targets[0].spaceName>"
108
+ }
109
+ ```
110
+ 输出:`✓ 已创建 skywalk-sdd/project-identity.json(project_id: <spaceKey>),请提交到 spec 仓 Git 以便团队共享`
111
+ 4. **已存在但 `project_id` 与 `targets[0].spaceKey` 不一致** → 用 AskUserQuestion 询问:
112
+ > "project-identity.json 中的 project_id 与当前选择的 KB Space spaceKey 不一致:
113
+ > - 文件中:`<existing project_id>`
114
+ > - 当前 Space:`<spaceKey>`
115
+ > 是否更新?"
116
+ - 用户确认 → 更新 `project_id` + `kb_space_id` + `kb_space_name`
117
+ - 用户拒绝 → 保留原值(可能入库时报 `PROJECT_SPACE_MISMATCH`)
118
+ 5. **已存在且一致** → 跳过,不输出
119
+
120
+ > state.json 字段 schema、鉴权细节、列表接口 → [reference.md](reference.md)。
121
+
122
+ ## 更换知识库
123
+
124
+ 当用户说「换知识库 / 换 Space / 重新选择」时:
125
+
126
+ 1. **保留 apiKey**(不需要重新输入密钥)
127
+ 2. 清空 `state.targets`
128
+ 3. 重走 Step 3(选择空间与知识库)
129
+ 4. 重走 Step 4(更新 project-identity.json)
130
+
131
+ > ⚠️ 更换 Space 后,`project_id` 会随之变化。之前上传到旧 Space 的归档包不受影响,但新上传将写入新 Space。
132
+
133
+ ## 查看当前配置
134
+
135
+ 读取 `kb-state.json` 并展示:
136
+
137
+ ```markdown
138
+ ### 当前 KB 配置
139
+ - API 地址:{api}
140
+ - 租户:{tenantKey}
141
+ - API Key:{前缀}****(已配置 / 未配置)
142
+ - 目标 Space/KB:
143
+ 1. {spaceName}(spaceKey: {spaceKey})→ {kbName}
144
+ 2. ...
145
+ - project-identity.json:{已存在 / 不存在}(project_id: {值})
146
+ ```
147
+
148
+ ## 用户口令
149
+
150
+ | 用户说 | Agent 做 |
151
+ |--------|----------|
152
+ | 配置知识库 / 首次使用 | 走完 Step 1→2→3→4 |
153
+ | 换密钥 / 重置 API Key | 清 apiKey,重走 Step 2 |
154
+ | 换空间 / 换知识库 / 重新选择 | 清 targets,重走 Step 3→4 |
155
+ | 看配置 / 当前配置 | 读取 kb-state.json + project-identity.json 并展示 |
156
+ | 校验配置 | 探活 + 校验 project_id === spaceKey |
157
+
158
+ ## 硬规则
159
+
160
+ - 无 `apiKey` 不得猜密钥、不得改走 login。
161
+ - 无 `targets` 不得臆造 spaceId/kbId。
162
+ - 完整 apiKey 只写共享 state 文件(`kb-state.json`,spec 仓根目录);聊天里最多显示前缀。
163
+ - 401/403 时清掉 `apiKey`,请用户重贴;勿循环重试。
164
+ - **共享状态**:state 文件与 `opsx-ontology-query`、`opsx-kb-ingest`、`kb-upload` 共用。配置一次,全部生效。
@@ -0,0 +1,116 @@
1
+ # 本体知识库配置 · 参考
2
+
3
+ 需要鉴权细节、state 字段或 API 示例时再读。
4
+
5
+ ## `kb-state.json`
6
+
7
+ 路径:`<spec-root>/kb-state.json`(已 gitignore)。
8
+
9
+ ```json
10
+ {
11
+ "api": "http://10.29.213.80:8080/api",
12
+ "tenantKey": "default",
13
+ "apiKey": "sk_sdd_…",
14
+ "updatedAt": "2026-07-19T12:00:00Z",
15
+ "targets": [
16
+ {
17
+ "spaceId": "uuid",
18
+ "spaceKey": "demo",
19
+ "spaceName": "演示空间",
20
+ "kbId": "uuid",
21
+ "kbName": "默认知识库"
22
+ }
23
+ ]
24
+ }
25
+ ```
26
+
27
+ | 字段 | 必填 | 说明 |
28
+ |------|------|------|
29
+ | `api` | 是 | API 根,含 `/api` |
30
+ | `tenantKey` | 是 | 列空间用;默认 `default` |
31
+ | `apiKey` | 是 | 控制台创建的 `sk_sdd_…`,scope 建议 `context:read` + `archive:ingest` |
32
+ | `targets` | 操作前必填 | 多选;可为空仅当尚未完成选择 |
33
+ | `updatedAt` | 建议 | ISO-8601 |
34
+
35
+ ## `project-identity.json`
36
+
37
+ 路径:`<spec-root>/skywalk-sdd/project-identity.json`(**提交到 Git,团队共享**)。
38
+
39
+ ```json
40
+ {
41
+ "schema_version": "kld-sdd-project-identity/v1",
42
+ "project_id": "<KB Space 的 spaceKey>",
43
+ "created_at": "<ISO timestamp>",
44
+ "kb_space_id": "<Space UUID>",
45
+ "kb_space_name": "<Space 名称>"
46
+ }
47
+ ```
48
+
49
+ | 字段 | 必填 | 说明 |
50
+ |------|------|------|
51
+ | `schema_version` | 是 | 固定 `kld-sdd-project-identity/v1` |
52
+ | `project_id` | 是 | = 目标 Space 的 `spaceKey`,KB 入库时校验一致性 |
53
+ | `created_at` | 是 | ISO-8601 |
54
+ | `kb_space_id` | 建议 | Space UUID |
55
+ | `kb_space_name` | 建议 | Space 名称 |
56
+
57
+ ## 鉴权
58
+
59
+ ```http
60
+ Authorization: Bearer sk_sdd_…
61
+ ```
62
+
63
+ - **不要** `POST /auth/login`。
64
+ - Key 在控制台「API 密钥」创建;创建时建议勾选 **读取上下文 / context:read** + **入库 / archive:ingest**(查询和入库都需要)。
65
+ - 401/403:清掉 state 里的 `apiKey`,请用户重贴;勿循环重试。
66
+
67
+ 探活与校验(PowerShell 用 `Invoke-RestMethod`,**禁止** `curl`):
68
+
69
+ ```powershell
70
+ # 探活
71
+ Invoke-RestMethod -Uri "$API/health"
72
+
73
+ # 校验 API Key + 列空间
74
+ $headers = @{ Authorization = "Bearer $API_KEY" }
75
+ Invoke-RestMethod -Uri "$API/v1/spaces?tenantKey=$TENANT_KEY" -Headers $headers
76
+ ```
77
+
78
+ ## 列表接口(选择用)
79
+
80
+ ```bash
81
+ # 空间
82
+ GET $API/v1/spaces?tenantKey=$TENANT_KEY
83
+
84
+ # 某空间下 KB
85
+ GET $API/v1/spaces/{spaceId}/knowledge-bases
86
+ ```
87
+
88
+ API Key 一般为租户范围;`apiKeySpaceId` 为空时可列出租户下多空间,便于多选。
89
+
90
+ ### 响应字段映射(重要)
91
+
92
+ KB API 返回的 JSON 字段名(Java record 序列化)与 `kb-state.json` 的字段名**不完全一致**,必须做映射:
93
+
94
+ #### Space 列表 → `kb-state.json targets[]`
95
+
96
+ | API 响应字段 (`ProjectSpaceDto`) | `kb-state.json` 字段 | 映射 |
97
+ |------|------|------|
98
+ | `spaceId` | `spaceId` | 直接使用 |
99
+ | `spaceKey` | `spaceKey` | 直接使用 |
100
+ | `name` | `spaceName` | **需映射** `name → spaceName` |
101
+
102
+ #### KB 列表 → `kb-state.json targets[]`
103
+
104
+ | API 响应字段 (`KnowledgeBaseDto`) | `kb-state.json` 字段 | 映射 |
105
+ |------|------|------|
106
+ | `knowledgeBaseId` | `kbId` | **需映射** `knowledgeBaseId → kbId` |
107
+ | `name` | `kbName` | **需映射** `name → kbName` |
108
+
109
+ #### Resolve 响应 → `semantic-identity` 参数
110
+
111
+ | API 响应字段 (`ResolveCandidate`) | `semantic-identity` 参数 | 注意 |
112
+ |------|------|------|
113
+ | `entityId` | `--entity-id` | KB 返回完整 UUID(36字符),`semantic-identity` 的 `id.cjs` 会自动归一化为 8-hex 短格式 |
114
+ | `entityVersionId` | `--previous-version-id` | 同上,自动归一化 |
115
+
116
+ > ⚠️ **常见错误**:直接用 `k.kbId` / `k.kbName` 读取 KB API 响应 → 返回空值。正确写法:`k.knowledgeBaseId` / `k.name`,然后映射到 `kb-state.json` 的 `kbId` / `kbName`。
@@ -9,59 +9,28 @@ description: >-
9
9
 
10
10
  # 本体知识库 · 入库
11
11
 
12
- 只负责**入库**(zip 上传)。鉴权只用 **API Key**(`Authorization: Bearer sk_sdd_…`),**禁止**走手机号登录。
12
+ 只负责**入库**(zip 上传 + 文件夹上传)。鉴权只用 **API Key**(`Authorization: Bearer sk_sdd_…`),**禁止**走手机号登录。
13
13
 
14
14
  > 控制台知识库页另有浮动 Ontology Agent(会话登录 + SSE);本 Skill 仍走 API Key,二者分开。
15
15
 
16
- > **📡 共享状态**:本技能与 `opsx-ontology-query` **共用同一份 KB 配置**(`../.shared/kb-state.json`)。任一 skill 配置后,另一个自动可用,无需重复输入 API Key。
16
+ > **📡 共享状态**:本技能与 `opsx-ontology-query`、`kb-upload` **共用同一份 KB 配置**(`kb-state.json`,位于 spec 仓根目录)。KB 配置(API Key + Space/KB 选择 + project-identity.json)由 `opsx-kb-config` 统一负责。
17
+
18
+ > **🖥️ 跨平台执行规则**(Windows 用户必读):
19
+ > - 入库涉及 HTTP 请求和 JSON 文件读写,**禁止**在 PowerShell 中使用 `curl` → 用 `Invoke-RestMethod` 或 Node.js 脚本(`kb-upload.cjs` 已封装)
20
+ > - **禁止**用 PowerShell `Get-Content` / `Out-File` 读写 JSON(默认写 BOM 导致 JSON.parse 失败)→ 用 write_to_file 工具或 Node.js `fs` 模块
21
+ > - **禁止** `| cat` 管道 → 直接执行命令
17
22
 
18
23
  ## Session 启动(每次用本 Skill 必做)
19
24
 
20
25
  ```
21
26
  Task Progress:
22
- - [ ] 1. 读 ../.shared/kb-state.json(没有则当空)
23
- - [ ] 2. 无 apiKey → 向用户索取并写入共享 state(勿把完整 key 打进聊天摘要)
24
- - [ ] 3. 无 targets 或用户要重置 → 拉空间/KB 列表,让用户多选后写入
25
- - [ ] 4. 按意图执行入库操作(上传 / 查状态 / 列表 / 重试)
26
- - [ ] 5. 按模板输出结果
27
+ - [ ] 1. 读 kb-state.json(spec 仓根目录)
28
+ - [ ] 2. 无 apiKey 或无 targets → 提示用户运行 /opsx-kb-config 配置知识库
29
+ - [ ] 3. 有配置 → 按意图执行入库操作(上传 / 查状态 / 列表 / 重试)
30
+ - [ ] 4. 按模板输出结果
27
31
  ```
28
32
 
29
- ### 1–2. API Key
30
-
31
- 若 `state.apiKey` 为空或无效(401/403):
32
-
33
- 1. 请用户提供 API Key(控制台「API 密钥」创建,至少含 `archive:ingest`。建议同时勾选 `context:read`,使 `opsx-ontology-query` 也能复用此 Key)。
34
- 2. 可选:请用户确认 `api`(默认 `http://localhost:8090/api`)与 `tenantKey`(默认 `default`)。
35
- 3. 写入共享 `../.shared/kb-state.json`(创建目录若不存在)。
36
- 4. 用 `GET $API/health` 探活;再用 `GET $API/v1/spaces?tenantKey=…` + Bearer 校验 key。
37
-
38
- 用户说「换密钥 / 重置 API Key」→ 清空 `apiKey`(可保留 targets),回到本步。
39
-
40
- ### 3. 选择空间与知识库(支持多选)
41
-
42
- 若 `state.targets` 为空,或用户说「重新选择 / 重置空间 / 重置知识库」:
43
-
44
- 1. `GET $API/v1/spaces?tenantKey=$TENANT_KEY`
45
- 2. 对每个相关 space:`GET $API/v1/spaces/{spaceId}/knowledge-bases`
46
- 3. 向用户展示「空间名 / spaceId → KB 名 / kbId」清单,**允许多选**。
47
- 4. 写入 `targets: [{ spaceId, spaceName, spaceKey, kbId, kbName }, …]`。
48
- 5. 仅清空 targets、保留 apiKey 即完成「重置空间和知识库」。
49
-
50
- 入库时:用户指定目标 KB(从 `targets` 中选择),仅对选中的 KB 执行上传。
51
-
52
- ### 3.5 兜底:确保 project-identity.json 存在(上传前必做)
53
-
54
- > 正常情况下 `project-identity.json` 已在 `opsx-ontology-query` 首次配置 KB 时创建。
55
- > 本步是兜底:如果用户跳过了 ontology-query(如选择 archive 降级路径),直接来做入库,需要确保文件存在且 `project_id` 正确。
56
-
57
- 1. 读取 spec 包裹包路径:
58
- ```bash
59
- SPEC_ROOT=$(node "$(cat .sdd-spec-root)/skywalk-sdd/ontology/cli.cjs" spec-root)
60
- ```
61
- 2. 检查 `$SPEC_ROOT/skywalk-sdd/project-identity.json`:
62
- - **不存在** → 从 `targets[0].spaceKey` 创建(格式同 `opsx-ontology-query` Step 3.5)
63
- - **已存在但 `project_id !== targets[0].spaceKey`** → 提示用户:`⚠️ project_id 与 Space spaceKey 不一致,入库将报 PROJECT_SPACE_MISMATCH。是否更新?`
64
- - **已存在且一致** → 跳过
33
+ > **KB 配置**(API Key、Space/KB 选择、project-identity.json)由 `opsx-kb-config` 统一负责。如用户需要配置或更换 KB,请引导运行 `/opsx-kb-config`。
65
34
 
66
35
  ### 4. 入库操作
67
36
 
@@ -122,11 +91,195 @@ curl -sS -X POST "$API/v1/spaces/$SPACE_ID/knowledge-bases/$KB_ID/ingestions/$JO
122
91
  - 包内无重复 `(external key, entity_id)` 绑定对
123
92
  - `project_id` / `spaceKey` 既有规则保留
124
93
  - **编号格式**:三条正则(见 reference)+ 场景 SLUG≤40 / 键总长≤120;先归一化再匹配
125
- - **场景作用域**:scenario 键的 REQ 前缀必须出现在同包 requirement 绑定集合
94
+ - **场景作用域**:scenario 键的需求编号前缀必须出现在同包 requirement 绑定集合
126
95
  - **功能配对**:同实体每条 feature 绑定必须能与某 requirement 条目的 `feature_id` 精确配对;反向同理
127
96
 
128
97
  详细字段说明 → [reference.md](reference.md)。
129
98
 
99
+ ### 4.5 文件夹上传(支持随时入库)
100
+
101
+ 支持直接传入 SDD 变更目录,自动完成语义校验、元数据生成、打包上传。**不要求 SDD 流程全部走完**,任意阶段均可入库。
102
+
103
+ **用法**:
104
+
105
+ ```bash
106
+ # 仅校验(不上传)
107
+ node skywalk-sdd/kb-upload.cjs --validate-only --folder=<变更目录路径> [--minimum-level=propose-spec]
108
+
109
+ # 文件夹上传(自动跑语义管线 + 生成元数据 + 打包 zip + 上传)
110
+ node skywalk-sdd/kb-upload.cjs --folder=<变更目录路径> [--minimum-level=propose-spec]
111
+
112
+ # zip 包上传(兼容现有方式)
113
+ node skywalk-sdd/kb-upload.cjs --package=<zip 路径>
114
+ ```
115
+
116
+ #### 上传等级(`--minimum-level`)
117
+
118
+ | 等级 | 最低要求 | 入库内容 | 适用场景 |
119
+ |------|---------|----------|---------|
120
+ | `propose-spec`(默认) | proposal.md + spec.md | Change、Capability、STMT、AC、Constraint | 需求定义阶段入库 |
121
+ | `design` | + design.md | + DesignElement | 补充技术方案 |
122
+ | `task` | + tasks.md | + Task、TaskDependency | 补充任务拆解 |
123
+ | `full` | 全部 4 个 | 全部实体 | 完整归档 |
124
+
125
+ #### 前置校验规则
126
+
127
+ - 校验必需文件存在(按 `--minimum-level` 决定)
128
+ - 校验 `project-identity.json` 存在且 `project_id === spaceKey`(本地预检,避免上传后报 `PROJECT_SPACE_MISMATCH`)
129
+ - 若 `project-identity.json` 不存在 → 提示运行 `/opsx-kb-config` 配置知识库
130
+
131
+ #### 重新入库前:KB 身份同步(重要)
132
+
133
+ 当 Change 的 proposal.md / spec.md / design.md / tasks.md **内容被修改后重新上传**(KB 中已存在同 entity-id 的实体),需要更新被修改实体的身份元数据(delta-state + version-id + predecessor-version),否则 KB 会拒绝(`VERSION_IDENTITY_CONFLICT`:同 version-id 但 content_hash 不同)。
134
+
135
+ > **KB 入库原理**:对每个实体执行 `INSERT … ON CONFLICT (entity_version_id) DO NOTHING`。若 version-id 已存在且 entity-id + content_hash 一致 → 幂等跳过 ✅;若 content_hash 不同 → `VERSION_IDENTITY_CONFLICT` ❌。因此,**未修改的实体不需要动**——它们 version-id + content_hash 都没变 → KB 自动幂等跳过。
136
+
137
+ > **语义校验**:`kb-upload.cjs --folder` 在上传前运行 `reconcileChange()` 语义校验。当本地无 confirmed archive(单变更场景)时,语义校验跳过基于历史的 lineage 检查,`modified` 实体只需提供合法的 `predecessor-version`(来自 KB resolve)即可通过。
138
+
139
+ **判断是否为重新入库**(满足任一即为重新入库):
140
+
141
+ - spec.md / proposal.md 身份块中已有 `entity-id` + `version-id`(说明之前已分配过身份)
142
+ - 上次 `kb-upload.cjs --folder` 返回 `succeeded`(KB 中已有该实体)
143
+ - 上传返回 `VERSION_IDENTITY_CONFLICT`(错误消息含 anchorId / name / versionId)
144
+
145
+ **重新入库步骤**(只针对**被修改的实体**):
146
+
147
+ > **⚠️ 文档实体也需要同步**:修改 .md 文件内容时,需同步**两类**实体:
148
+ > 1. **结构实体**(Artifact / DocumentSection)— 存储在 `ontology/ontology-identities.json` 中,由 reconcile 管理
149
+ > 2. **业务实体**(STMT / AC / CON / CAP)— 身份块写在 markdown 中
150
+ >
151
+ > 常见错误:只同步了业务实体(AC/STMT),遗漏了结构实体(文档 Artifact)→ KB 报 `VERSION_IDENTITY_CONFLICT`。
152
+
153
+ **方式 A:自动同步(推荐)**
154
+
155
+ ```bash
156
+ # 自动扫描 content_hash 变化,同步所有变更的结构实体 + 内容有变化的业务实体
157
+ # (内容驱动模式:只同步 content_hash 真正变化的实体,不污染未变实体)
158
+ node skywalk-sdd/kb-sync-identity.cjs \
159
+ --file=spec.md \
160
+ --change=<change-key> \
161
+ --project=.
162
+
163
+ # 或精准指定变更的业务实体锚点(用户明确知道改了哪些 AC/STMT/CON)
164
+ node skywalk-sdd/kb-sync-identity.cjs \
165
+ --file=spec.md \
166
+ --change=<change-key> \
167
+ --project=. \
168
+ --anchors=AC-USER-CRUD-006
169
+
170
+ # 干跑模式(只看报告不修改文件)
171
+ node skywalk-sdd/kb-sync-identity.cjs \
172
+ --file=spec.md \
173
+ --change=<change-key> \
174
+ --project=. \
175
+ --dry-run
176
+
177
+ # 跳过 KB 验证(KB 不可用时强制本地同步,风险:predecessor 可能不匹配 KB)
178
+ node skywalk-sdd/kb-sync-identity.cjs \
179
+ --file=spec.md \
180
+ --change=<change-key> \
181
+ --project=. \
182
+ --skip-kb-verify
183
+ ```
184
+
185
+ 脚本自动完成:
186
+ - **Phase 0**: 读取 KB 配置(kb-state.json + project-identity.json)→ KB API 健康检查 → KB resolve 验证实体当前 version-id
187
+ - **结构实体同步**: 扫描 `ontology-identities.json` 中结构实体(Artifact + DocumentSection)的 content_hash 变化,为变更实体生成新 version-id(predecessor=KB验证的 version-id)
188
+ - **业务实体同步**: 基于结构实体同步的 DocumentSection 变化结果,只为 content_hash 真正变化的业务实体生成新 version-id 并更新 markdown 身份块。未变化的 `added` 实体保持原样,不会被无端 version-bump
189
+ - **同步报告**: 输出哪些实体被同步、哪些跳过、predecessor 来源(local/KB)
190
+
191
+ > **✅ 内容驱动安全保证**:默认模式下,脚本通过 Part 1 的 DocumentSection content_hash 比较结果驱动 Part 2 的业务实体筛选。只有 `delta-state=added` **且** 对应 DocumentSection content_hash 真正变化的实体才会被转为 `modified`。未变化的 `added` 实体保持原样,不会造成版本污染。
192
+ >
193
+ > **⚠️ 精准模式** (`--anchors`): 当用户明确知道改了哪些实体时使用。脚本信任用户指定,即使 Part 1 未检测到 content_hash 变化也会同步(适用于身份元数据变更等非内容性修改场景)。
194
+
195
+ > **Phase 0 说明**:脚本默认会查询 KB 获取实体的当前 version-id 作为 predecessor。如果本地 version-id 与 KB 不一致(如 git 恢复后),脚本会自动使用 KB 的 version-id 作为 predecessor 并输出警告。如果 KB 不可达,脚本会降级为本地模式(使用本地 version-id)并输出警告。使用 `--skip-kb-verify` 可强制跳过所有 KB 操作。
196
+
197
+ > ⛔ **禁止**跳过同步直接 `kb-upload.cjs --folder`:被修改的实体 version-id 未变 + content_hash 变了 → KB 报 `VERSION_IDENTITY_CONFLICT`。
198
+ >
199
+ > ⛔ **禁止**删除整个 `ontology/` + `artifacts/` 缓存来绕过冲突:会导致所有实体被重新计算 hash 和 version-id,造成不必要的版本扩散。应使用 `kb-sync-identity.cjs` 精准同步。
200
+
201
+ **方式 B:手动同步(脚本不可用时 fallback)**
202
+
203
+ 1. **识别被修改的实体**——Agent 知道自己改了哪些内容。例如:修改了 AC-USER-CRUD-007 的默认分页大小,则只需处理该实体。若修改了 spec.md 的多处内容,列出所有受影响的实体 anchor(如 STMT-xxx、AC-xxx、CON-xxx)。
204
+
205
+ > ⚠️ **不要忘记结构实体**:修改了 .md 文件后,该文件的 Artifact 实体(文档级)也需要同步。结构实体存储在 `ontology/ontology-identities.json` 中,需更新其 `version_id` 字段。
206
+
207
+ 2. **对每个被修改的实体,查 KB → 生成新 version-id → 更新身份块**
208
+
209
+ 2a. 查询 KB 获取该实体的当前 version-id:
210
+
211
+ ```bash
212
+ # 示例:查询 AC-USER-CRUD-007(external-id 为场景键)
213
+ node "$(cat .sdd-spec-root)/skywalk-sdd/context-client.cjs" \
214
+ --mode=resolve \
215
+ --external-system=requirement-mgmt \
216
+ --external-object-type=scenario \
217
+ --external-id="REQ-US-2026-001:SCN-user-crud-007" \
218
+ --space-id="$ENGINEERING_KB_SPACE_ID" \
219
+ --kb-id="$ENGINEERING_KB_KB_ID"
220
+ ```
221
+
222
+ KB 返回 `candidates[]`,每项含 `entityId` 和 `entityVersionId`(完整 UUID,`id.cjs` 自动归一化为 8-hex)。
223
+
224
+ > 没有 `external-ref` 的实体(如 STMT、CON)可用 `--entity-id=<spec.md中的entity-id>` + `--entity-type=<SpecificationStatement|Constraint>` 查询。
225
+
226
+ 2b. 生成新 version-id:
227
+
228
+ ```bash
229
+ # AC-USER-CRUD-007,KB 返回 entityId=e248ba63, entityVersionId=be5581d9
230
+ node "$(cat .sdd-spec-root)/skywalk-sdd/log.cjs" semantic-identity \
231
+ --delta-state=modified \
232
+ --entity-id=e248ba63 \
233
+ --predecessor-version=be5581d9
234
+ # → 输出新的 version_id(如 3f8a1c2b)
235
+ ```
236
+
237
+ 2c. 更新 spec.md / proposal.md 中该实体的身份块:
238
+
239
+ ```markdown
240
+ ##### 场景:[AC-USER-CRUD-007] 默认分页查询
241
+ - **entity-id**: e248ba63 ← 不变(复用 KB 的 entityId)
242
+ - **version-id**: 3f8a1c2b ← 改为步骤 2b 生成的新 version-id
243
+ - **delta-state**: modified ← 从 added 改为 modified
244
+ - **predecessor-version**: be5581d9 ← 新增:KB 返回的 entityVersionId
245
+ - **external-ref**: requirement-mgmt:scenario:REQ-US-2026-001:SCN-user-crud-007
246
+ ```
247
+
248
+ 3. **未修改的实体保持原样不动**——它们 version-id + content_hash 都没变 → KB 自动幂等跳过。不需要改为 `unchanged` 或 `modified`。
249
+
250
+ 4. **先校验再上传**:
251
+
252
+ ```bash
253
+ # 先校验(含语义校验,确认 modified 实体身份块正确)
254
+ node skywalk-sdd/kb-upload.cjs --validate-only --folder=<变更目录路径>
255
+
256
+ # 校验通过后上传
257
+ node skywalk-sdd/kb-upload.cjs --folder=<变更目录路径>
258
+ ```
259
+
260
+ > ⛔ **禁止**跳过步骤 1-3 直接 `kb-upload.cjs --folder`:被修改的实体 version-id 未变 + content_hash 变了 → KB 报 `VERSION_IDENTITY_CONFLICT`(错误消息含 anchorId / name / versionId,据此定位是哪个实体)。
261
+
262
+ **其他影响入库的因素**:
263
+
264
+ | 因素 | 说明 | 是否需 Agent 处理 |
265
+ |------|------|-------------------|
266
+ | `archive_id` | `kb-upload.cjs` 每次自动生成新的 | ❌ 自动处理 |
267
+ | `project_id` | 必须匹配 Space 的 `spaceKey`,前置校验已拦截 | ❌ 已校验 |
268
+ | `external-ref` 冲突 | 同 external-key 已绑不同 entity-id → `EXTERNAL_REF_CONFLICT` | ✅ 见下文错误处理 |
269
+ | `ARCHIVE_IMMUTABILITY_VIOLATION` | 同一 archive_id 上传不同 content_hash | ❌ 不会发生(每次新 archive_id) |
270
+ | 多个实体同时修改 | 每个被修改的实体都需步骤 2 | ✅ 对每个分别处理 |
271
+
272
+ #### 文件夹上传 vs Zip 上传
273
+
274
+ | 维度 | 文件夹上传 | Zip 上传 |
275
+ |------|-----------|---------|
276
+ | 输入 | 原始变更目录 | 已打包 zip |
277
+ | 语义管线 | 自动执行(semantic-scan → reconcile → check) | 无需(zip 已含元数据) |
278
+ | 元数据生成 | 自动生成(archive-manifest + canonical-facts + conversion-report) | 无需(zip 已含) |
279
+ | 适用场景 | 任意 SDD 阶段入库 | 已有 zip 补传 / 归档后上传 |
280
+
281
+ > 文件夹上传最终也会打包成 zip 走同一上传接口,KB 后端解析逻辑不变。
282
+
130
283
  ### 5. 输出模板
131
284
 
132
285
  ```markdown
@@ -149,11 +302,23 @@ curl -sS -X POST "$API/v1/spaces/$SPACE_ID/knowledge-bases/$KB_ID/ingestions/$JO
149
302
 
150
303
  成功时展示 `report.externalRefsWritten`。冲突**不**出现在成功模板。
151
304
 
305
+ 若 `errorCode=VERSION_IDENTITY_CONFLICT`(version-id 与 KB 已锁定版本冲突):
306
+
307
+ 1. 错误消息中包含 `anchorId`、`name`、`entityId`、`versionId`,据此**定位是哪个实体**被修改后未更新身份。
308
+ 2. 按「重新入库前:KB 身份同步」步骤操作:对该实体查询 KB → 生成新 version-id → 更新 delta-state=modified + predecessor-version → 重新上传。
309
+ 3. **只需处理该实体**,其余未修改实体不用动(KB 自动幂等跳过)。
310
+
311
+ 若语义校验阶段报 `SEM_VERSION_LINEAGE_MISSING`(`modified` 实体无法定位前序版本):
312
+
313
+ 1. 说明本地存在 confirmed archive(如通过 SDD archive 流程归档的其他变更),但 markdown 中 `predecessor-version` 与 archive 中的 version-id 不匹配。
314
+ 2. 检查 `predecessor-version` 是否来自 KB resolve 返回的 `entityVersionId`(而非手写)。
315
+ 3. 按「重新入库前:KB 身份同步」步骤 2a 重新查询 KB,确认 `entityVersionId`,然后重新生成 version-id 并更新身份块。
316
+
152
317
  若 `errorCode=EXTERNAL_REF_CONFLICT`(整包已回滚):
153
318
 
154
319
  1. 展示 `report.details`(externalRef / anchor / existingEntityId / incomingEntityId)。
155
320
  2. 向用户给出两个修正选项:
156
- - **复用历史身份**:回到 kld-sdd,复用 KB 中原有 entity_id / current version,重新 check → 归档 → 入库
321
+ - **复用历史身份**:按「重新入库前:KB 身份同步」步骤——查询 KB 获取已有 entity-id 和 version-id,更新 delta-state=modified + predecessor-version,重新上传
157
322
  - **改为新锚点**:回到 kld-sdd,分配新锚点与新 entity_id,重新 check → 归档 → 入库
158
323
  3. **禁止**在 KB 内现场改绑或解绑。
159
324
 
@@ -161,8 +326,8 @@ curl -sS -X POST "$API/v1/spaces/$SPACE_ID/knowledge-bases/$KB_ID/ingestions/$JO
161
326
 
162
327
  | errorCode | 指引 |
163
328
  |-----------|------|
164
- | `EXTERNAL_REFERENCE_FORMAT_INVALID` | 编号不合规 → **回需求管理系统换发**;不得手改编号硬闯 |
165
- | `EXTERNAL_REFERENCE_SCOPE_MISMATCH` | 场景键 REQ 前缀不在申报集合 → 回 kld-sdd spec 修正 external-ref |
329
+ | `EXTERNAL_REFERENCE_FORMAT_INVALID` | 编号含非白名单字符 → **检查外部编号来源**;不得手改编号硬闯 |
330
+ | `EXTERNAL_REFERENCE_SCOPE_MISMATCH` | 场景键需求编号前缀不在申报集合 → 回 kld-sdd spec 修正 external-ref |
166
331
  | `EXTERNAL_REFERENCE_FEATURE_UNBOUND` | feature↔requirement.`feature_id` 配对断裂 → 回 propose/archive 修正线缆字段 |
167
332
 
168
333
  **禁止**手改包内编号绕过门禁。
@@ -173,18 +338,17 @@ curl -sS -X POST "$API/v1/spaces/$SPACE_ID/knowledge-bases/$KB_ID/ingestions/$JO
173
338
  - 无 `targets` 不得臆造 spaceId/kbId。
174
339
  - 上传前确认 zip 包路径存在且为有效 zip 文件。
175
340
  - 入库失败时展示 `errorCode` 和 `errorMessage`,不编造原因。
176
- - 完整 apiKey 只写共享 state 文件(`../.shared/kb-state.json`);聊天里最多显示前缀(如 `sk_sdd_****`)。
341
+ - 完整 apiKey 只写共享 state 文件(`kb-state.json`,spec 仓根目录);聊天里最多显示前缀(如 `sk_sdd_****`)。
177
342
  - 401/403 时清掉 `apiKey`,请用户重贴;勿循环重试。
178
- - **共享状态**:state 文件与 `opsx-ontology-query` 共用。如用户此前已通过查询 skill 配置过 KB,本 skill 启动时直接读取已有配置,无需重复询问。
343
+ - **共享状态**:state 文件与 `opsx-ontology-query`、`kb-upload`、`opsx-kb-config` 共用。KB 配置由 `opsx-kb-config` 统一负责。
179
344
 
180
345
  ## 用户口令
181
346
 
182
347
  | 用户说 | Agent 做 |
183
348
  |--------|----------|
184
- | (首次使用) | 要 key → 选 KB(多选)→ 写共享 state → 再操作。此后 `opsx-ontology-query` 也自动可用 |
185
- | 换密钥 / 重置 API Key | 清 apiKey,重走第 2 步 |
186
- | 重新选择 / 重置空间或知识库 | 清 targets,重走第 3 步 |
187
- | 上传 / 入库 | 用当前共享 targets 中选中的 KB 上传 zip |
349
+ | 配置 / 换密钥 / 换 KB | 引导运行 `/opsx-kb-config`(本技能不处理配置) |
350
+ | 上传 zip / 入库 | 用当前共享 targets 中选中的 KB 上传 zip |
351
+ | 文件夹上传 / 随时入库 | 运行 `kb-upload.cjs --folder=<path>`,自动打包上传 |
188
352
  | 查状态 / 看任务 | 用 jobId 查询任务状态 |
189
353
  | 列任务 / 看历史 | 列出最近入库任务 |
190
354
  | 重试 | 对失败的 jobId 执行重试 |
@@ -192,6 +356,7 @@ curl -sS -X POST "$API/v1/spaces/$SPACE_ID/knowledge-bases/$KB_ID/ingestions/$JO
192
356
  ## 心智模型(简述)
193
357
 
194
358
  - 入库是同步操作:上传后后端立即解析 zip、校验、写入数据库,返回最终 job 状态。
359
+ - 投影是异步操作:入库成功后,后端异步执行 vector embedding + AGE graph 投影(通常 5-30 秒)。正常 SDD 流程中,阶段间由用户手动触发,间隔远大于投影时间,无需特殊处理。
195
360
  - 幂等机制:同一 `archive_id` + `content_hash` 重复上传会命中幂等,不重复写入。
196
361
  - `archive_id` 不可变:同一 `archive_id` 上传不同 `content_hash` 会被拒绝(`ARCHIVE_IMMUTABILITY_VIOLATION`)。
197
362
  - `project_id` 必须匹配:zip 包 manifest 中的 `project_id` 必须等于目标 Space 的 `spaceKey`。