kld-sdd 2.6.13 → 2.6.14

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.
package/README.md CHANGED
@@ -69,7 +69,7 @@ node skywalk-sdd/log.cjs semantic-status --project=. --change=<name>
69
69
  node skywalk-sdd/log.cjs semantic-observe --project=. --change=<name>
70
70
  ```
71
71
 
72
- `propose/spec/design/task` 在文档写入完成时就必须同步工作态 JSON,不等待 Check 或 Archive。各阶段的 `stage_end` 会自动执行一次 reconcile 作为无 Hook 环境的兜底;Skill 中的显式 `semantic-reconcile` 与该兜底幂等。每份 Markdown 的结构化结果写入 `openspec/changes/<name>/artifacts/<原相对路径>.ontology.json`,`openspec/changes/<name>/artifact-index.json` 与 `working-ontology.json` 提供统一索引与全量工作态视图;这些文件随 change 目录一起提交和归档。`skywalk-sdd/state/ontology/` 仅保留 revision 指针与跨进程锁,默认不提交。
72
+ `propose/spec/design/task` 在文档写入完成时就必须同步工作态 JSON,不等待 Check 或 Archive。各阶段的 `stage_end` 会自动执行一次 reconcile 作为无 Hook 环境的兜底;Skill 中的显式 `semantic-reconcile` 与该兜底幂等。每份 Markdown 的结构化结果写入 `openspec/changes/<name>/artifacts/<原相对路径>.ontology.json`,聚合索引与全量工作态写入 `openspec/changes/<name>/ontology/`(含 `artifact-index.json`、`working-ontology.json` 等);这些文件随 change 目录一起提交和归档。`skywalk-sdd/state/ontology/` 仅保留 revision 指针与跨进程锁,默认不提交。
73
73
 
74
74
  工作态 JSON 使用 `kld-sdd-working-artifact-facts/v1`,明确标记 `canonical=false` 和 `review_status=draft|pending`。它用于当前 Change 的渐进式展开、定向查询和跨文档校验,不能直接入知识库。Check 重新解析原文、校验并在全部通过时生成 pending revision;Archive 再次对账后才确认、转换为 `canonical-facts.json` 并打包。
75
75
 
@@ -83,7 +83,7 @@ node skywalk-sdd/log.cjs semantic-observe --project=. --change=<name>
83
83
  | `full` | 强制 STMT→AC→Design→Task 全链和 Task DAG |
84
84
  | `strict` | 在 full 基础上要求完整语义来源 |
85
85
 
86
- 同一 Change 的作者阶段同步、observe、reconcile、check 和 archive 共用跨进程事务锁。`working-ontology.json`、diagnostics、file index、artifact index 及所有分文件 JSON 在 change 目录内原子提交,随后切换 `skywalk-sdd/state/ontology/<change>/current.json` 指针;读取时会拒绝任一文件缺失或 revision 撕裂。
86
+ 同一 Change 的作者阶段同步、observe、reconcile、check 和 archive 共用跨进程事务锁。`ontology/working-ontology.json`、diagnostics、file index、artifact index 及所有分文件 JSON 在 change 目录内原子提交,随后切换 `skywalk-sdd/state/ontology/<change>/current.json` 指针;读取时会拒绝任一文件缺失或 revision 撕裂。
87
87
 
88
88
  Archive 使用两阶段事务:先复制到 staging,从 staging 重新解析正文并生成本体快照、canonical facts、转换报告和 v2 manifest,所有步骤成功后才切换为正式 archive。归档完成后同时生成相邻的 `<日期>-<name>.zip`,其中 `archive-manifest.json` 的文件清单和 SHA-256 必须精确覆盖包内文件,可直接交给知识库消费。语义校验、快照、协议转换、打包或 Spec 同步失败时,活动 Change 会保留或恢复,不留下伪 confirmed 半成品。
89
89
 
@@ -286,11 +286,13 @@ your-project/
286
286
  │ └── changes/
287
287
  │ └── <name>/
288
288
  │ ├── proposal.md
289
- │ ├── working-ontology.json
290
- │ ├── artifact-index.json
291
- │ ├── diagnostics.json
292
- │ ├── file-index.json
293
- │ ├── ontology-identities.json
289
+ │ ├── ontology/
290
+ ├── working-ontology.json
291
+ ├── artifact-index.json
292
+ ├── diagnostics.json
293
+ ├── file-index.json
294
+ │ │ ├── ontology-identities.json
295
+ │ │ └── continuity-resolution.json
294
296
  │ └── artifacts/
295
297
  │ ├── proposal.ontology.json
296
298
  │ └── specs/<capability>/
@@ -680,7 +680,7 @@ npx kld-sdd --version">复制</button>
680
680
  <div class="top"><span class="idx">01</span><h3>opsx-propose <span class="arg">&lt;变更名称&gt;</span></h3><span class="role">Why · 业务意图</span></div>
681
681
  <p>创建业务意图文档,明确“为什么做”。除痛点、目标、影响模块与约束外,还会让你确认 <b>Full / Simple 模式、测试策略和 change-type</b>(config / document / report / composite / other),并写入稳定的 <code>change-id</code>。</p>
682
682
  <p>若提供 REQ / FEAT 外部编号,会通过 <code>opsx-ontology-query</code> 做 Continuity 查询,识别新增、延续或迭代能力;不会把本地 archive 当成跨迭代权威。</p>
683
- <div class="out">proposal.md + artifacts/proposal.ontology.json + working-ontology.json</div>
683
+ <div class="out">proposal.md + artifacts/proposal.ontology.json + ontology/working-ontology.json</div>
684
684
  </div>
685
685
 
686
686
  <div class="skill reveal" id="sk-spec" style="border-left-color:#38bdf8">
@@ -824,7 +824,7 @@ npx kld-sdd --version">复制</button>
824
824
  <article class="feature-card reveal">
825
825
  <div class="eyebrow">Materialize</div>
826
826
  <h3>写完即生成工作态事实</h3>
827
- <p>propose/spec/design/task 每次写入后立即 reconcile,产出 <code>artifacts/**/*.ontology.json</code>、<code>artifact-index.json</code> 与 <code>working-ontology.json</code>,状态为 draft/pending,尚不能直接入库。</p>
827
+ <p>propose/spec/design/task 每次写入后立即 reconcile,产出 <code>artifacts/**/*.ontology.json</code>、<code>ontology/artifact-index.json</code> 与 <code>ontology/working-ontology.json</code>,状态为 draft/pending,尚不能直接入库。</p>
828
828
  </article>
829
829
  <article class="feature-card reveal">
830
830
  <div class="eyebrow">Traceability</div>
@@ -913,9 +913,8 @@ node skywalk-sdd/log.cjs semantic-observe --project=. --change=add-user-auth">
913
913
  │ │ ├── <span class="file">proposal.md</span> <span class="cmt"># mode / change-id / change-type / test-strategy</span>
914
914
  │ │ ├── <span class="dir">specs/&lt;capability&gt;/</span> <span class="cmt"># Full:spec / design / tasks</span>
915
915
  │ │ ├── <span class="file">spec · design · tasks.md</span> <span class="cmt"># Simple:根目录三文档</span>
916
- │ │ ├── <span class="file">working-ontology.json</span>
917
- │ │ ├── <span class="dir">artifact-index/ · diagnostics/ · file-index/</span>
918
- │ │ ├── <span class="dir">ontology-identities/ · artifacts/**/*.ontology.json</span>
916
+ │ │ ├── <span class="dir">ontology/</span> <span class="cmt"># working-ontology · artifact-index · diagnostics · file-index · identities · continuity-resolution</span>
917
+ │ │ ├── <span class="dir">artifacts/**/*.ontology.json</span>
919
918
  │ │ └── <span class="file">logs/execution-log.md</span>
920
919
  │ └── <span class="dir">archive/</span>
921
920
  │ ├── <span class="dir">&lt;date&gt;-&lt;name&gt;/</span>
package/lib/init.js CHANGED
@@ -831,18 +831,14 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
831
831
  console.log(` ⚠️ V3 指标模块源文件缺失: ${metricsV3Src}(report/metrics 可能无法加载 metrics-v3)`);
832
832
  }
833
833
 
834
- const runtimeMetadataSrc = path.join(pkgPath, 'skywalk-sdd', 'runtime-metadata.cjs');
834
+ const packageVersion = require(path.join(pkgPath, 'package.json')).version;
835
835
  const runtimeMetadataDst = path.join(dataDir, 'runtime-metadata.cjs');
836
- if (fs.existsSync(runtimeMetadataSrc)) {
837
- fs.copyFileSync(runtimeMetadataSrc, runtimeMetadataDst);
838
- const packageVersion = require(path.join(pkgPath, 'package.json')).version;
839
- fs.writeFileSync(path.join(dataDir, 'runtime-metadata.json'), `${JSON.stringify({
840
- schema_version: 'kld-sdd-runtime-metadata/v1',
841
- package_name: '@hunterzheng/kld-sdd',
842
- version: packageVersion,
843
- }, null, 2)}\n`, 'utf8');
844
- console.log(` ✓ 部署 kld-sdd 运行时身份(${packageVersion})`);
845
- }
836
+ fs.writeFileSync(
837
+ runtimeMetadataDst,
838
+ `'use strict';\nmodule.exports = { resolveRuntimeMetadata() { return { version: '${packageVersion}', producer: 'kld-sdd@${packageVersion}' }; } };\n`,
839
+ 'utf8',
840
+ );
841
+ console.log(` ✓ 部署 kld-sdd 运行时身份(${packageVersion})`);
846
842
 
847
843
  const reportingSrc = path.join(pkgPath, 'skywalk-sdd', 'reporting');
848
844
  const reportingDst = path.join(dataDir, 'reporting');
@@ -887,6 +883,7 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
887
883
  console.log(' ✓ openspec: node skywalk-sdd/openspec-shim.cjs list(自动 cd 到 *-sdd-specs)');
888
884
  console.log(' ✓ Spec Context: node skywalk-sdd/context-client.cjs --query="<自然语言需求>" --target-stage=spec');
889
885
  console.log(' ✓ Apply worktree: record-base 在 §1.5;收尾 apply-worktree-finish.cjs --change=<name>');
886
+ console.log(' ℹ️ project-identity.json 将在首次配置 KB(opsx-ontology-query)时自动创建');
890
887
  console.log('✅ Telemetry 已就绪(数据存储在 skywalk-sdd/,无需配置 MCP)');
891
888
  return true;
892
889
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kld-sdd",
3
- "version": "2.6.13",
3
+ "version": "2.6.14",
4
4
  "description": "KLD SDD OpenSpec 项目初始化工具 - 一键部署 SDD skills",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -65,10 +65,16 @@ function ensureProjectIdentity(projectRoot) {
65
65
  }
66
66
  return existing;
67
67
  }
68
+ // 兜底:project-identity.json 未通过 opsx-ontology-query 配置 KB 时创建
69
+ // 此时 project_id 为随机值,KB 入库时会因 project_id !== spaceKey 而失败
70
+ console.warn('⚠️ project-identity.json 不存在 — 未通过 opsx-ontology-query 配置 KB。');
71
+ console.warn('⚠️ 将生成临时 project_id,KB 入库时需确保与 Space spaceKey 一致。');
72
+ console.warn('⚠️ 建议在首次使用 opsx-ontology-query 配置 KB 时自动创建正确的 project-identity.json。');
68
73
  const identity = {
69
74
  schema_version: PROJECT_IDENTITY_SCHEMA_VERSION,
70
75
  project_id: `urn:kld:sdd:project:${generateUuidV7()}`,
71
76
  created_at: new Date().toISOString(),
77
+ _warning: 'UNCONFIGURED — 请通过 opsx-ontology-query 配置 KB 后更新 project_id 为 Space spaceKey',
72
78
  };
73
79
  writeJson(identityPath, identity);
74
80
  return identity;
@@ -5,6 +5,10 @@ const path = require('path');
5
5
  const { parseChangeArtifacts } = require('./artifact-parser.cjs');
6
6
  const { normalizeFacts } = require('./normalizer.cjs');
7
7
  const { DIAGNOSTIC_CODES } = require('./schema.cjs');
8
+ const {
9
+ ONTOLOGY_STATE_FILES,
10
+ resolveOntologyStateFile,
11
+ } = require('./ontology-paths.cjs');
8
12
 
9
13
  function catalogDiagnostic(code, message, context = {}) {
10
14
  return {
@@ -97,11 +101,14 @@ function validateArchiveCandidate(candidate, facts) {
97
101
  return { eligible: false, snapshot, manifest, diagnostics };
98
102
  }
99
103
 
100
- const artifactIndexPath = path.join(candidate.changeDir, 'artifact-index.json');
104
+ const artifactIndexPath = resolveOntologyStateFile(
105
+ candidate.changeDir,
106
+ ONTOLOGY_STATE_FILES.artifactIndex,
107
+ );
101
108
  if (!fs.existsSync(artifactIndexPath)) {
102
109
  diagnostics.push(catalogDiagnostic(
103
110
  DIAGNOSTIC_CODES.ARCHIVE_NOT_CONFIRMED,
104
- `Archive 缺少 change 目录下的 artifact-index.json: ${candidate.change}`,
111
+ `Archive 缺少 change 目录下的 ontology/artifact-index.json: ${candidate.change}`,
105
112
  { file: artifactIndexPath, suggestion: '通过 KLD-SDD reconcile/archive 在 change 目录生成工作态 JSON 后再归档' },
106
113
  ));
107
114
  return { eligible: false, snapshot, manifest, diagnostics };
@@ -0,0 +1,73 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const ONTOLOGY_STATE_DIR = 'ontology';
7
+
8
+ const ONTOLOGY_STATE_FILES = {
9
+ working: 'working-ontology.json',
10
+ diagnostics: 'diagnostics.json',
11
+ fileIndex: 'file-index.json',
12
+ artifactIndex: 'artifact-index.json',
13
+ identities: 'ontology-identities.json',
14
+ continuityResolution: 'continuity-resolution.json',
15
+ };
16
+
17
+ function ontologyStateRelative(basename) {
18
+ return `${ONTOLOGY_STATE_DIR}/${basename}`;
19
+ }
20
+
21
+ function changeOntologyFilePaths(changeDir) {
22
+ const resolved = path.resolve(changeDir);
23
+ const ontologyDir = path.join(resolved, ONTOLOGY_STATE_DIR);
24
+ return {
25
+ changeDir: resolved,
26
+ ontologyDir,
27
+ working: path.join(ontologyDir, ONTOLOGY_STATE_FILES.working),
28
+ diagnostics: path.join(ontologyDir, ONTOLOGY_STATE_FILES.diagnostics),
29
+ fileIndex: path.join(ontologyDir, ONTOLOGY_STATE_FILES.fileIndex),
30
+ artifactIndex: path.join(ontologyDir, ONTOLOGY_STATE_FILES.artifactIndex),
31
+ continuityResolution: path.join(ontologyDir, ONTOLOGY_STATE_FILES.continuityResolution),
32
+ identities: path.join(ontologyDir, ONTOLOGY_STATE_FILES.identities),
33
+ artifactDir: path.join(resolved, 'artifacts'),
34
+ };
35
+ }
36
+
37
+ function resolveOntologyStateFile(changeDir, basename) {
38
+ const resolved = path.resolve(changeDir);
39
+ const modern = path.join(resolved, ONTOLOGY_STATE_DIR, basename);
40
+ if (fs.existsSync(modern)) return modern;
41
+ const legacy = path.join(resolved, basename);
42
+ if (fs.existsSync(legacy)) return legacy;
43
+ return modern;
44
+ }
45
+
46
+ function resolveRevisionBundlePaths(changeDir) {
47
+ const preferred = changeOntologyFilePaths(changeDir);
48
+ return {
49
+ ...preferred,
50
+ working: resolveOntologyStateFile(changeDir, ONTOLOGY_STATE_FILES.working),
51
+ diagnostics: resolveOntologyStateFile(changeDir, ONTOLOGY_STATE_FILES.diagnostics),
52
+ fileIndex: resolveOntologyStateFile(changeDir, ONTOLOGY_STATE_FILES.fileIndex),
53
+ artifactIndex: resolveOntologyStateFile(changeDir, ONTOLOGY_STATE_FILES.artifactIndex),
54
+ };
55
+ }
56
+
57
+ function removeLegacyOntologyStateFiles(changeDir) {
58
+ const resolved = path.resolve(changeDir);
59
+ for (const basename of Object.values(ONTOLOGY_STATE_FILES)) {
60
+ const legacy = path.join(resolved, basename);
61
+ if (fs.existsSync(legacy)) fs.rmSync(legacy, { force: true });
62
+ }
63
+ }
64
+
65
+ module.exports = {
66
+ ONTOLOGY_STATE_DIR,
67
+ ONTOLOGY_STATE_FILES,
68
+ ontologyStateRelative,
69
+ changeOntologyFilePaths,
70
+ resolveOntologyStateFile,
71
+ resolveRevisionBundlePaths,
72
+ removeLegacyOntologyStateFiles,
73
+ };
@@ -20,6 +20,14 @@ const {
20
20
  assertChangeLock,
21
21
  releaseChangeLock,
22
22
  } = require('./change-lock.cjs');
23
+ const {
24
+ ONTOLOGY_STATE_FILES,
25
+ ontologyStateRelative,
26
+ changeOntologyFilePaths,
27
+ resolveOntologyStateFile,
28
+ resolveRevisionBundlePaths,
29
+ removeLegacyOntologyStateFiles,
30
+ } = require('./ontology-paths.cjs');
23
31
 
24
32
  function sha256(value) {
25
33
  return crypto.createHash('sha256').update(value).digest('hex');
@@ -31,15 +39,7 @@ function resolveChangeDir(projectRoot, changeName) {
31
39
  }
32
40
 
33
41
  function changeOntologyPaths(projectRoot, changeName) {
34
- const changeDir = resolveChangeDir(projectRoot, changeName);
35
- return {
36
- changeDir,
37
- working: path.join(changeDir, 'working-ontology.json'),
38
- diagnostics: path.join(changeDir, 'diagnostics.json'),
39
- fileIndex: path.join(changeDir, 'file-index.json'),
40
- artifactIndex: path.join(changeDir, 'artifact-index.json'),
41
- artifactDir: path.join(changeDir, 'artifacts'),
42
- };
42
+ return changeOntologyFilePaths(resolveChangeDir(projectRoot, changeName));
43
43
  }
44
44
 
45
45
  function statePaths(projectRoot, changeName) {
@@ -160,14 +160,16 @@ function commitRevision(projectRoot, changeName, result, lock) {
160
160
  assertChangeLock(lock);
161
161
  const basePaths = statePaths(projectRoot, changeName);
162
162
  const paths = changeOntologyPaths(projectRoot, changeName);
163
+ const readablePaths = resolveRevisionBundlePaths(paths.changeDir);
163
164
  fs.mkdirSync(basePaths.dir, { recursive: true });
164
165
  fs.mkdirSync(paths.changeDir, { recursive: true });
166
+ fs.mkdirSync(paths.ontologyDir, { recursive: true });
165
167
 
166
- if (fs.existsSync(paths.working)) {
167
- const existing = readJson(paths.working, 'SEM_STATE_CORRUPT');
168
+ if (fs.existsSync(readablePaths.working)) {
169
+ const existing = readJson(readablePaths.working, 'SEM_STATE_CORRUPT');
168
170
  if (existing.revision === result.revision) {
169
- readRevisionBundle(paths, result.revision);
170
- const artifactIndex = readJson(paths.artifactIndex, 'SEM_STATE_CORRUPT');
171
+ readRevisionBundle(readablePaths, result.revision);
172
+ const artifactIndex = readJson(readablePaths.artifactIndex, 'SEM_STATE_CORRUPT');
171
173
  return {
172
174
  changed: false,
173
175
  paths: {
@@ -225,6 +227,7 @@ function commitRevision(projectRoot, changeName, result, lock) {
225
227
  atomicWriteIfChanged(path.join(paths.changeDir, artifactFile.path), fs.readFileSync(stagedPath, 'utf8'));
226
228
  }
227
229
  pruneStaleArtifacts(paths.changeDir, artifactBundle.index);
230
+ removeLegacyOntologyStateFiles(paths.changeDir);
228
231
  fs.rmSync(stagingDir, { recursive: true, force: true });
229
232
 
230
233
  const pointer = {
@@ -232,7 +235,7 @@ function commitRevision(projectRoot, changeName, result, lock) {
232
235
  change: safeChangeName(changeName),
233
236
  revision: result.revision,
234
237
  change_dir: path.relative(path.resolve(projectRoot), paths.changeDir).replace(/\\/g, '/'),
235
- artifact_index: 'artifact-index.json',
238
+ artifact_index: ontologyStateRelative(ONTOLOGY_STATE_FILES.artifactIndex),
236
239
  };
237
240
  assertChangeLock(lock);
238
241
  const changed = atomicWriteIfChanged(basePaths.current, pointer);
@@ -272,9 +275,9 @@ function scanChange(projectRoot, changeName, options = {}) {
272
275
  identityHistory,
273
276
  effectiveGraph,
274
277
  projectRoot,
275
- continuityResolutionPath: path.join(
278
+ continuityResolutionPath: resolveOntologyStateFile(
276
279
  parsed.changeDir,
277
- 'continuity-resolution.json',
280
+ ONTOLOGY_STATE_FILES.continuityResolution,
278
281
  ),
279
282
  });
280
283
  const reviewStatus = options.markPending && validation.valid ? 'pending' : 'draft';
@@ -358,16 +361,17 @@ function reconcileChange(projectRoot, changeName, options = {}) {
358
361
  function readWorkingState(projectRoot, changeName) {
359
362
  const basePaths = statePaths(projectRoot, changeName);
360
363
  const paths = changeOntologyPaths(projectRoot, changeName);
364
+ const readablePaths = resolveRevisionBundlePaths(paths.changeDir);
361
365
  if (fs.existsSync(basePaths.current)) {
362
366
  const pointer = readJson(basePaths.current, 'SEM_STATE_POINTER_CORRUPT');
363
367
  if (!pointer.revision || pointer.change !== safeChangeName(changeName)) {
364
368
  throw stateError('SEM_STATE_POINTER_CORRUPT', `current.json 不属于 Change ${changeName}`);
365
369
  }
366
- return readRevisionBundle(paths, pointer.revision).working;
370
+ return readRevisionBundle(readablePaths, pointer.revision).working;
367
371
  }
368
- if (!fs.existsSync(paths.working)) return null;
369
- const working = readJson(paths.working, 'SEM_STATE_CORRUPT');
370
- readRevisionBundle(paths, working.revision);
372
+ if (!fs.existsSync(readablePaths.working)) return null;
373
+ const working = readJson(readablePaths.working, 'SEM_STATE_CORRUPT');
374
+ readRevisionBundle(readablePaths, working.revision);
371
375
  return working;
372
376
  }
373
377
 
@@ -396,9 +400,12 @@ function createArchiveSnapshot(projectRoot, changeName, archiveDir, inputState)
396
400
  if (!state.facts_hash || state.facts_hash !== actualFacts.facts_hash) {
397
401
  throw new Error(`归档正文与待确认本体事实不一致: ${changeName}`);
398
402
  }
399
- const artifactIndexPath = path.join(targetDir, 'artifact-index.json');
403
+ const artifactIndexPath = resolveOntologyStateFile(
404
+ targetDir,
405
+ ONTOLOGY_STATE_FILES.artifactIndex,
406
+ );
400
407
  if (!fs.existsSync(artifactIndexPath)) {
401
- throw new Error(`归档目录缺少 artifact-index.json: ${targetDir}`);
408
+ throw new Error(`归档目录缺少 ontology/artifact-index.json: ${targetDir}`);
402
409
  }
403
410
  const artifactIndex = readJson(artifactIndexPath, 'SEM_STATE_CORRUPT');
404
411
  if (artifactIndex.revision !== state.revision) {
@@ -468,4 +475,7 @@ module.exports = {
468
475
  reconcileChange,
469
476
  readWorkingState,
470
477
  createArchiveSnapshot,
478
+ resolveOntologyStateFile,
479
+ resolveRevisionBundlePaths,
480
+ ONTOLOGY_STATE_FILES,
471
481
  };
@@ -4,15 +4,20 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
  const { SCHEMA_VERSION } = require('./schema.cjs');
6
6
  const { generateUuidV7, isValidUuidV7 } = require('./id.cjs');
7
+ const {
8
+ ONTOLOGY_STATE_FILES,
9
+ ontologyStateRelative,
10
+ resolveOntologyStateFile,
11
+ } = require('./ontology-paths.cjs');
7
12
 
8
- const REGISTRY_FILE = 'ontology-identities.json';
13
+ const REGISTRY_FILE = ontologyStateRelative(ONTOLOGY_STATE_FILES.identities);
9
14
 
10
15
  function registryPath(changeDir) {
11
16
  return path.join(path.resolve(changeDir), REGISTRY_FILE);
12
17
  }
13
18
 
14
19
  function readRegistry(changeDir) {
15
- const filePath = registryPath(changeDir);
20
+ const filePath = resolveOntologyStateFile(changeDir, ONTOLOGY_STATE_FILES.identities);
16
21
  if (!fs.existsSync(filePath)) {
17
22
  return { schema_version: SCHEMA_VERSION, identities: {} };
18
23
  }
@@ -35,6 +40,10 @@ function writeRegistry(changeDir, registry) {
35
40
  const tempPath = `${filePath}.${process.pid}.tmp`;
36
41
  fs.writeFileSync(tempPath, content, 'utf8');
37
42
  fs.renameSync(tempPath, filePath);
43
+ const legacy = path.join(path.resolve(changeDir), ONTOLOGY_STATE_FILES.identities);
44
+ if (legacy !== filePath && fs.existsSync(legacy)) {
45
+ fs.rmSync(legacy, { force: true });
46
+ }
38
47
  return true;
39
48
  }
40
49
 
@@ -1,11 +1,18 @@
1
1
  'use strict';
2
2
 
3
+ const path = require('path');
4
+ const fs = require('fs');
3
5
  const {
4
6
  DIAGNOSTIC_CODES,
5
7
  normalizeProfile,
6
8
  relationDefinition,
7
9
  } = require('./schema.cjs');
8
10
  const { isValidEntityId, isValidUuid } = require('./id.cjs');
11
+ const {
12
+ ONTOLOGY_STATE_FILES,
13
+ ontologyStateRelative,
14
+ resolveOntologyStateFile,
15
+ } = require('./ontology-paths.cjs');
9
16
 
10
17
  function diagnostic(code, severity, message, context = {}) {
11
18
  return {
@@ -350,18 +357,19 @@ function validateContinuityAndExternalRefs(facts, diagnostics, options = {}) {
350
357
  const kind = String(continuity.kind || '').toLowerCase();
351
358
  const resolutionPath = options.continuityResolutionPath
352
359
  || (facts.change
353
- ? require('path').join(
354
- options.projectRoot || process.cwd(),
355
- 'openspec',
356
- 'changes',
357
- facts.change,
358
- 'continuity-resolution.json',
360
+ ? resolveOntologyStateFile(
361
+ path.join(
362
+ options.projectRoot || process.cwd(),
363
+ 'openspec',
364
+ 'changes',
365
+ facts.change,
366
+ ),
367
+ ONTOLOGY_STATE_FILES.continuityResolution,
359
368
  )
360
369
  : '');
361
370
  let resolution = null;
362
371
  if (resolutionPath) {
363
372
  try {
364
- const fs = require('fs');
365
373
  if (fs.existsSync(resolutionPath)) {
366
374
  resolution = JSON.parse(fs.readFileSync(resolutionPath, 'utf8'));
367
375
  }
@@ -392,7 +400,7 @@ function validateContinuityAndExternalRefs(facts, diagnostics, options = {}) {
392
400
  'error',
393
401
  'Continuity=iteration 但 continuity-resolution.json 缺失或仍为 pending',
394
402
  {
395
- file: resolutionPath || 'continuity-resolution.json',
403
+ file: resolutionPath || ontologyStateRelative(ONTOLOGY_STATE_FILES.continuityResolution),
396
404
  suggestion: '在 propose/spec 完成 A/B/C 决议后再 check',
397
405
  },
398
406
  ));
@@ -3,6 +3,7 @@
3
3
  const crypto = require('crypto');
4
4
  const path = require('path');
5
5
  const { resolveRuntimeMetadata } = require('../runtime-metadata.cjs');
6
+ const { ontologyStateRelative, ONTOLOGY_STATE_FILES } = require('./ontology-paths.cjs');
6
7
 
7
8
  const WORKING_ARTIFACT_SCHEMA_VERSION = 'kld-sdd-working-artifact-facts/v1';
8
9
  const ARTIFACT_INDEX_SCHEMA_VERSION = 'kld-sdd-working-artifact-index/v1';
@@ -228,7 +229,7 @@ function buildWorkingArtifactFacts(result, options = {}) {
228
229
  review_status: reviewStatus,
229
230
  valid: Boolean(result.valid),
230
231
  revision: result.revision,
231
- working_ontology_path: 'working-ontology.json',
232
+ working_ontology_path: ontologyStateRelative(ONTOLOGY_STATE_FILES.working),
232
233
  artifacts: artifactIndex,
233
234
  },
234
235
  files: artifactFiles,