scene-capability-engine 3.0.0

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 (336) hide show
  1. package/CHANGELOG.md +2513 -0
  2. package/LICENSE +21 -0
  3. package/README.md +765 -0
  4. package/README.zh.md +630 -0
  5. package/bin/kiro-spec-engine.js +796 -0
  6. package/bin/kse.js +3 -0
  7. package/bin/sce.js +3 -0
  8. package/bin/sco.js +3 -0
  9. package/docs/331-poc-adaptation-roadmap.md +156 -0
  10. package/docs/331-poc-dual-track-integration-guide.md +120 -0
  11. package/docs/331-poc-weekly-delivery-checklist.md +52 -0
  12. package/docs/OFFLINE_INSTALL.md +96 -0
  13. package/docs/README.md +279 -0
  14. package/docs/adopt-migration-guide.md +599 -0
  15. package/docs/adoption-guide.md +616 -0
  16. package/docs/agent-hooks-analysis.md +815 -0
  17. package/docs/architecture.md +733 -0
  18. package/docs/articles/ai-driven-development-philosophy-and-practice-review.md +208 -0
  19. package/docs/articles/ai-driven-development-philosophy-and-practice.en.md +459 -0
  20. package/docs/articles/ai-driven-development-philosophy-and-practice.md +492 -0
  21. package/docs/autonomous-control-guide.md +851 -0
  22. package/docs/command-reference.md +1368 -0
  23. package/docs/community.md +115 -0
  24. package/docs/cross-tool-guide.md +555 -0
  25. package/docs/developer-guide.md +619 -0
  26. package/docs/document-governance.md +865 -0
  27. package/docs/environment-management-guide.md +526 -0
  28. package/docs/examples/add-export-command/design.md +194 -0
  29. package/docs/examples/add-export-command/requirements.md +110 -0
  30. package/docs/examples/add-export-command/tasks.md +88 -0
  31. package/docs/examples/add-rest-api/design.md +855 -0
  32. package/docs/examples/add-rest-api/requirements.md +323 -0
  33. package/docs/examples/add-rest-api/tasks.md +355 -0
  34. package/docs/examples/add-user-dashboard/design.md +192 -0
  35. package/docs/examples/add-user-dashboard/requirements.md +143 -0
  36. package/docs/examples/add-user-dashboard/tasks.md +91 -0
  37. package/docs/faq.md +697 -0
  38. package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.json +156 -0
  39. package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.md +24 -0
  40. package/docs/images/wechat-qr.png +0 -0
  41. package/docs/integration-modes.md +529 -0
  42. package/docs/integration-philosophy.md +313 -0
  43. package/docs/knowledge-management-guide.md +263 -0
  44. package/docs/manual-workflows-guide.md +418 -0
  45. package/docs/moqui-capability-matrix.md +73 -0
  46. package/docs/moqui-template-core-library-playbook.md +109 -0
  47. package/docs/multi-agent-coordination-guide.md +553 -0
  48. package/docs/multi-repo-management-guide.md +1344 -0
  49. package/docs/quick-start-with-ai-tools.md +375 -0
  50. package/docs/quick-start.md +146 -0
  51. package/docs/release-checklist.md +121 -0
  52. package/docs/releases/README.md +13 -0
  53. package/docs/releases/v1.46.2-validation.md +45 -0
  54. package/docs/releases/v1.46.2.md +50 -0
  55. package/docs/scene-runtime-guide.md +347 -0
  56. package/docs/spec-collaboration-guide.md +369 -0
  57. package/docs/spec-locking-guide.md +225 -0
  58. package/docs/spec-numbering-guide.md +348 -0
  59. package/docs/spec-workflow.md +519 -0
  60. package/docs/steering-strategy-guide.md +196 -0
  61. package/docs/team-collaboration-guide.md +465 -0
  62. package/docs/testing-strategy.md +272 -0
  63. package/docs/tools/claude-guide.md +654 -0
  64. package/docs/tools/cursor-guide.md +706 -0
  65. package/docs/tools/generic-guide.md +446 -0
  66. package/docs/tools/kiro-guide.md +308 -0
  67. package/docs/tools/vscode-guide.md +445 -0
  68. package/docs/tools/windsurf-guide.md +391 -0
  69. package/docs/troubleshooting.md +1135 -0
  70. package/docs/upgrade-guide.md +639 -0
  71. package/docs/value-observability-guide.md +127 -0
  72. package/docs/zh/README.md +341 -0
  73. package/docs/zh/quick-start.md +764 -0
  74. package/docs/zh/release-checklist.md +121 -0
  75. package/docs/zh/releases/README.md +13 -0
  76. package/docs/zh/releases/v1.46.2-validation.md +45 -0
  77. package/docs/zh/releases/v1.46.2.md +50 -0
  78. package/docs/zh/spec-numbering-guide.md +348 -0
  79. package/docs/zh/tools/claude-guide.md +349 -0
  80. package/docs/zh/tools/cursor-guide.md +281 -0
  81. package/docs/zh/tools/generic-guide.md +499 -0
  82. package/docs/zh/tools/kiro-guide.md +342 -0
  83. package/docs/zh/tools/vscode-guide.md +449 -0
  84. package/docs/zh/tools/windsurf-guide.md +378 -0
  85. package/docs/zh/value-observability-guide.md +127 -0
  86. package/docs//344/272/244/344/273/230/346/270/205/345/215/225.md +75 -0
  87. package/lib/adoption/adoption-logger.js +487 -0
  88. package/lib/adoption/adoption-strategy.js +538 -0
  89. package/lib/adoption/backup-manager.js +420 -0
  90. package/lib/adoption/conflict-resolver.js +410 -0
  91. package/lib/adoption/detection-engine.js +275 -0
  92. package/lib/adoption/diff-viewer.js +226 -0
  93. package/lib/adoption/error-formatter.js +509 -0
  94. package/lib/adoption/file-classifier.js +385 -0
  95. package/lib/adoption/progress-reporter.js +534 -0
  96. package/lib/adoption/smart-orchestrator.js +470 -0
  97. package/lib/adoption/strategy-selector.js +218 -0
  98. package/lib/adoption/summary-generator.js +493 -0
  99. package/lib/adoption/template-sync.js +605 -0
  100. package/lib/auto/autonomous-engine.js +485 -0
  101. package/lib/auto/checkpoint-manager.js +300 -0
  102. package/lib/auto/close-loop-runner.js +2476 -0
  103. package/lib/auto/config-schema.js +176 -0
  104. package/lib/auto/decision-engine.js +344 -0
  105. package/lib/auto/error-recovery-manager.js +580 -0
  106. package/lib/auto/goal-decomposer.js +278 -0
  107. package/lib/auto/progress-tracker.js +502 -0
  108. package/lib/auto/safety-manager.js +186 -0
  109. package/lib/auto/semantic-decomposer.js +137 -0
  110. package/lib/auto/state-manager.js +126 -0
  111. package/lib/auto/task-queue-manager.js +340 -0
  112. package/lib/backup/backup-system.js +372 -0
  113. package/lib/backup/selective-backup.js +207 -0
  114. package/lib/collab/agent-registry.js +240 -0
  115. package/lib/collab/collab-manager.js +285 -0
  116. package/lib/collab/contract-manager.js +320 -0
  117. package/lib/collab/coordinator.js +370 -0
  118. package/lib/collab/dependency-manager.js +280 -0
  119. package/lib/collab/index.js +20 -0
  120. package/lib/collab/integration-manager.js +202 -0
  121. package/lib/collab/merge-coordinator.js +252 -0
  122. package/lib/collab/metadata-manager.js +233 -0
  123. package/lib/collab/multi-agent-config.js +120 -0
  124. package/lib/collab/spec-lifecycle-manager.js +304 -0
  125. package/lib/collab/sync-barrier.js +88 -0
  126. package/lib/collab/visualizer.js +208 -0
  127. package/lib/commands/adopt.js +749 -0
  128. package/lib/commands/auto.js +19559 -0
  129. package/lib/commands/collab.js +275 -0
  130. package/lib/commands/context.js +99 -0
  131. package/lib/commands/docs.js +808 -0
  132. package/lib/commands/doctor.js +273 -0
  133. package/lib/commands/env.js +420 -0
  134. package/lib/commands/knowledge.js +309 -0
  135. package/lib/commands/lock.js +235 -0
  136. package/lib/commands/ops.js +409 -0
  137. package/lib/commands/orchestrate.js +446 -0
  138. package/lib/commands/prompt.js +105 -0
  139. package/lib/commands/repo.js +118 -0
  140. package/lib/commands/rollback.js +219 -0
  141. package/lib/commands/scene.js +15549 -0
  142. package/lib/commands/spec-bootstrap.js +147 -0
  143. package/lib/commands/spec-gate.js +157 -0
  144. package/lib/commands/spec-pipeline.js +205 -0
  145. package/lib/commands/status.js +321 -0
  146. package/lib/commands/task.js +199 -0
  147. package/lib/commands/templates.js +654 -0
  148. package/lib/commands/upgrade.js +231 -0
  149. package/lib/commands/value.js +569 -0
  150. package/lib/commands/watch.js +684 -0
  151. package/lib/commands/workflows.js +240 -0
  152. package/lib/commands/workspace-multi.js +325 -0
  153. package/lib/commands/workspace.js +189 -0
  154. package/lib/context/context-exporter.js +378 -0
  155. package/lib/context/prompt-generator.js +482 -0
  156. package/lib/data/moqui-capability-lexicon.json +45 -0
  157. package/lib/environment/backup-system.js +189 -0
  158. package/lib/environment/environment-manager.js +379 -0
  159. package/lib/environment/environment-registry.js +168 -0
  160. package/lib/gitignore/gitignore-backup.js +229 -0
  161. package/lib/gitignore/gitignore-detector.js +239 -0
  162. package/lib/gitignore/gitignore-integration.js +267 -0
  163. package/lib/gitignore/gitignore-transformer.js +193 -0
  164. package/lib/gitignore/layered-rules-template.js +42 -0
  165. package/lib/governance/archive-tool.js +284 -0
  166. package/lib/governance/cleanup-tool.js +237 -0
  167. package/lib/governance/config-manager.js +186 -0
  168. package/lib/governance/diagnostic-engine.js +271 -0
  169. package/lib/governance/doc-reference-checker.js +200 -0
  170. package/lib/governance/execution-logger.js +243 -0
  171. package/lib/governance/file-scanner.js +285 -0
  172. package/lib/governance/hooks-manager.js +333 -0
  173. package/lib/governance/reporter.js +337 -0
  174. package/lib/governance/validation-engine.js +181 -0
  175. package/lib/i18n.js +79 -0
  176. package/lib/knowledge/entry-manager.js +208 -0
  177. package/lib/knowledge/index-manager.js +261 -0
  178. package/lib/knowledge/knowledge-manager.js +273 -0
  179. package/lib/knowledge/template-manager.js +191 -0
  180. package/lib/lock/index.js +21 -0
  181. package/lib/lock/lock-file.js +192 -0
  182. package/lib/lock/lock-manager.js +321 -0
  183. package/lib/lock/machine-identifier.js +135 -0
  184. package/lib/lock/steering-file-lock.js +207 -0
  185. package/lib/lock/task-lock-manager.js +345 -0
  186. package/lib/operations/audit-logger.js +293 -0
  187. package/lib/operations/feedback-manager.js +1147 -0
  188. package/lib/operations/index.js +23 -0
  189. package/lib/operations/models/index.js +170 -0
  190. package/lib/operations/operations-manager.js +151 -0
  191. package/lib/operations/operations-validator.js +280 -0
  192. package/lib/operations/permission-manager.js +354 -0
  193. package/lib/operations/template-loader.js +143 -0
  194. package/lib/orchestrator/agent-spawner.js +629 -0
  195. package/lib/orchestrator/bootstrap-prompt-builder.js +236 -0
  196. package/lib/orchestrator/index.js +19 -0
  197. package/lib/orchestrator/orchestration-engine.js +1270 -0
  198. package/lib/orchestrator/orchestrator-config.js +173 -0
  199. package/lib/orchestrator/status-monitor.js +591 -0
  200. package/lib/python-checker.js +209 -0
  201. package/lib/repo/config-manager.js +580 -0
  202. package/lib/repo/errors/config-error.js +13 -0
  203. package/lib/repo/errors/git-error.js +15 -0
  204. package/lib/repo/errors/repo-error.js +14 -0
  205. package/lib/repo/git-operations.js +181 -0
  206. package/lib/repo/handlers/.gitkeep +1 -0
  207. package/lib/repo/handlers/exec-handler.js +155 -0
  208. package/lib/repo/handlers/health-handler.js +169 -0
  209. package/lib/repo/handlers/init-handler.js +197 -0
  210. package/lib/repo/handlers/status-handler.js +176 -0
  211. package/lib/repo/output-formatter.js +184 -0
  212. package/lib/repo/path-resolver.js +178 -0
  213. package/lib/repo/repo-manager.js +514 -0
  214. package/lib/scene-runtime/audit-emitter.js +59 -0
  215. package/lib/scene-runtime/binding-plugin-loader.js +351 -0
  216. package/lib/scene-runtime/binding-registry.js +349 -0
  217. package/lib/scene-runtime/eval-bridge.js +44 -0
  218. package/lib/scene-runtime/index.js +19 -0
  219. package/lib/scene-runtime/moqui-adapter.js +620 -0
  220. package/lib/scene-runtime/moqui-client.js +606 -0
  221. package/lib/scene-runtime/moqui-extractor.js +2029 -0
  222. package/lib/scene-runtime/plan-compiler.js +208 -0
  223. package/lib/scene-runtime/policy-gate.js +58 -0
  224. package/lib/scene-runtime/runtime-executor.js +358 -0
  225. package/lib/scene-runtime/scene-loader.js +96 -0
  226. package/lib/scene-runtime/scene-ontology.js +959 -0
  227. package/lib/scene-runtime/scene-template-linter.js +852 -0
  228. package/lib/scene-runtime/templates/scene-template-erp-query-v0.1.yaml +28 -0
  229. package/lib/scene-runtime/templates/scene-template-hybrid-shadow-v0.1.yaml +34 -0
  230. package/lib/spec/bootstrap/context-collector.js +48 -0
  231. package/lib/spec/bootstrap/draft-generator.js +158 -0
  232. package/lib/spec/bootstrap/questionnaire-engine.js +70 -0
  233. package/lib/spec/bootstrap/trace-emitter.js +59 -0
  234. package/lib/spec/multi-spec-orchestrate.js +93 -0
  235. package/lib/spec/pipeline/constants.js +6 -0
  236. package/lib/spec/pipeline/stage-adapters.js +118 -0
  237. package/lib/spec/pipeline/stage-runner.js +146 -0
  238. package/lib/spec/pipeline/state-store.js +119 -0
  239. package/lib/spec-gate/engine/gate-engine.js +165 -0
  240. package/lib/spec-gate/policy/default-policy.js +22 -0
  241. package/lib/spec-gate/policy/policy-loader.js +103 -0
  242. package/lib/spec-gate/result-emitter.js +81 -0
  243. package/lib/spec-gate/rules/default-rules.js +156 -0
  244. package/lib/spec-gate/rules/rule-registry.js +51 -0
  245. package/lib/steering/adoption-config.js +164 -0
  246. package/lib/steering/compliance-auto-fixer.js +204 -0
  247. package/lib/steering/compliance-cache.js +99 -0
  248. package/lib/steering/compliance-error-reporter.js +70 -0
  249. package/lib/steering/context-sync-manager.js +273 -0
  250. package/lib/steering/index.js +92 -0
  251. package/lib/steering/spec-steering.js +230 -0
  252. package/lib/steering/steering-compliance-checker.js +73 -0
  253. package/lib/steering/steering-loader.js +144 -0
  254. package/lib/steering/steering-manager.js +289 -0
  255. package/lib/task/index.js +12 -0
  256. package/lib/task/task-claimer.js +489 -0
  257. package/lib/task/task-status-store.js +418 -0
  258. package/lib/templates/cache-manager.js +440 -0
  259. package/lib/templates/content-generalizer.js +247 -0
  260. package/lib/templates/frontmatter-generator.js +128 -0
  261. package/lib/templates/git-handler.js +471 -0
  262. package/lib/templates/metadata-collector.js +328 -0
  263. package/lib/templates/path-utils.js +144 -0
  264. package/lib/templates/registry-parser.js +505 -0
  265. package/lib/templates/spec-reader.js +216 -0
  266. package/lib/templates/template-applicator.js +249 -0
  267. package/lib/templates/template-creator.js +256 -0
  268. package/lib/templates/template-error.js +143 -0
  269. package/lib/templates/template-exporter.js +502 -0
  270. package/lib/templates/template-manager.js +782 -0
  271. package/lib/templates/template-validator.js +361 -0
  272. package/lib/upgrade/migration-engine.js +382 -0
  273. package/lib/upgrade/migrations/.gitkeep +52 -0
  274. package/lib/upgrade/migrations/1.0.0-to-1.1.0.js +78 -0
  275. package/lib/utils/file-diff.js +177 -0
  276. package/lib/utils/fs-utils.js +274 -0
  277. package/lib/utils/tool-detector.js +383 -0
  278. package/lib/utils/validation.js +324 -0
  279. package/lib/value/gate-summary-emitter.js +99 -0
  280. package/lib/value/metric-contract-loader.js +210 -0
  281. package/lib/value/risk-evaluator.js +117 -0
  282. package/lib/value/weekly-snapshot-builder.js +61 -0
  283. package/lib/version/version-checker.js +156 -0
  284. package/lib/version/version-manager.js +327 -0
  285. package/lib/watch/action-executor.js +458 -0
  286. package/lib/watch/event-debouncer.js +323 -0
  287. package/lib/watch/execution-logger.js +550 -0
  288. package/lib/watch/file-watcher.js +499 -0
  289. package/lib/watch/presets.js +266 -0
  290. package/lib/watch/watch-manager.js +533 -0
  291. package/lib/workspace/multi/global-config.js +150 -0
  292. package/lib/workspace/multi/index.js +22 -0
  293. package/lib/workspace/multi/path-utils.js +173 -0
  294. package/lib/workspace/multi/workspace-context-resolver.js +244 -0
  295. package/lib/workspace/multi/workspace-registry.js +196 -0
  296. package/lib/workspace/multi/workspace-state-manager.js +537 -0
  297. package/lib/workspace/multi/workspace.js +90 -0
  298. package/lib/workspace/workspace-manager.js +370 -0
  299. package/lib/workspace/workspace-sync.js +356 -0
  300. package/locales/en.json +114 -0
  301. package/locales/zh.json +114 -0
  302. package/package.json +102 -0
  303. package/template/.kiro/README.md +247 -0
  304. package/template/.kiro/hooks/check-spec-on-create.kiro.hook +17 -0
  305. package/template/.kiro/hooks/run-tests-on-save.kiro.hook +13 -0
  306. package/template/.kiro/hooks/sync-tasks-on-edit.kiro.hook +16 -0
  307. package/template/.kiro/specs/SPEC_WORKFLOW_GUIDE.md +134 -0
  308. package/template/.kiro/steering/CORE_PRINCIPLES.md +133 -0
  309. package/template/.kiro/steering/CURRENT_CONTEXT.md +30 -0
  310. package/template/.kiro/steering/ENVIRONMENT.md +35 -0
  311. package/template/.kiro/steering/RULES_GUIDE.md +46 -0
  312. package/template/.kiro/templates/operations/default/change-impact.md +112 -0
  313. package/template/.kiro/templates/operations/default/deployment.md +91 -0
  314. package/template/.kiro/templates/operations/default/feedback-response.md +269 -0
  315. package/template/.kiro/templates/operations/default/migration-plan.md +172 -0
  316. package/template/.kiro/templates/operations/default/monitoring.md +135 -0
  317. package/template/.kiro/templates/operations/default/operations.md +135 -0
  318. package/template/.kiro/templates/operations/default/rollback.md +143 -0
  319. package/template/.kiro/templates/operations/default/tools.yaml +364 -0
  320. package/template/.kiro/templates/operations/default/troubleshooting.md +123 -0
  321. package/template/.kiro/tools/backup_manager.py +295 -0
  322. package/template/.kiro/tools/configuration_manager.py +218 -0
  323. package/template/.kiro/tools/document_evaluator.py +550 -0
  324. package/template/.kiro/tools/enhancement_logger.py +168 -0
  325. package/template/.kiro/tools/error_handler.py +335 -0
  326. package/template/.kiro/tools/improvement_identifier.py +444 -0
  327. package/template/.kiro/tools/modification_applicator.py +737 -0
  328. package/template/.kiro/tools/quality_gate_enforcer.py +207 -0
  329. package/template/.kiro/tools/quality_scorer.py +305 -0
  330. package/template/.kiro/tools/report_generator.py +154 -0
  331. package/template/.kiro/tools/ultrawork_enhancer.py +676 -0
  332. package/template/.kiro/tools/ultrawork_enhancer_refactored.py +0 -0
  333. package/template/.kiro/tools/ultrawork_enhancer_v2.py +463 -0
  334. package/template/.kiro/tools/ultrawork_enhancer_v3.py +606 -0
  335. package/template/.kiro/tools/workflow_quality_gate.py +100 -0
  336. package/template/README.md +111 -0
@@ -0,0 +1,959 @@
1
+ 'use strict';
2
+
3
+ const VALID_RELATION_TYPES = ['depends_on', 'composes', 'extends', 'produces'];
4
+
5
+ class OntologyGraph {
6
+ constructor() {
7
+ this._nodes = new Map();
8
+ this._edges = new Map();
9
+ }
10
+
11
+ addNode(ref, metadata = {}) {
12
+ if (typeof ref !== 'string' || !ref) {
13
+ throw new Error('Node ref must be a non-empty string');
14
+ }
15
+ this._nodes.set(ref, { ref, metadata });
16
+ }
17
+
18
+ getNode(ref) {
19
+ return this._nodes.get(ref) || null;
20
+ }
21
+
22
+ getAllNodes() {
23
+ return Array.from(this._nodes.values());
24
+ }
25
+
26
+ addEdge(sourceRef, targetRef, relationType) {
27
+ if (!VALID_RELATION_TYPES.includes(relationType)) {
28
+ throw new Error(
29
+ `Invalid relation type "${relationType}". Valid types: ${VALID_RELATION_TYPES.join(', ')}`
30
+ );
31
+ }
32
+
33
+ const missingNodes = [];
34
+ if (!this._nodes.has(sourceRef)) {
35
+ missingNodes.push(sourceRef);
36
+ }
37
+ if (!this._nodes.has(targetRef)) {
38
+ missingNodes.push(targetRef);
39
+ }
40
+ if (missingNodes.length > 0) {
41
+ throw new Error(
42
+ `Cannot add edge: node(s) not found: ${missingNodes.join(', ')}`
43
+ );
44
+ }
45
+
46
+ const edges = this._edges.get(sourceRef) || [];
47
+ edges.push({ source: sourceRef, target: targetRef, type: relationType });
48
+ this._edges.set(sourceRef, edges);
49
+ }
50
+
51
+ getEdges(ref) {
52
+ return this._edges.get(ref) || [];
53
+ }
54
+
55
+ getAllEdges() {
56
+ const all = [];
57
+ for (const edges of this._edges.values()) {
58
+ all.push(...edges);
59
+ }
60
+ return all;
61
+ }
62
+
63
+ toJSON() {
64
+ return {
65
+ nodes: this.getAllNodes(),
66
+ edges: this.getAllEdges()
67
+ };
68
+ }
69
+
70
+ static fromJSON(json) {
71
+ const graph = new OntologyGraph();
72
+ if (json && Array.isArray(json.nodes)) {
73
+ for (const node of json.nodes) {
74
+ graph.addNode(node.ref, node.metadata || {});
75
+ }
76
+ }
77
+ if (json && Array.isArray(json.edges)) {
78
+ for (const edge of json.edges) {
79
+ graph.addEdge(edge.source, edge.target, edge.type);
80
+ }
81
+ }
82
+ return graph;
83
+ }
84
+
85
+ }
86
+
87
+ /**
88
+ * 验证 OntologyGraph 一致性
89
+ * @param {OntologyGraph} graph
90
+ * @returns {{ valid: boolean, errors: Array<{ code: string, message: string, details: object }> }}
91
+ */
92
+ function validateOntology(graph) {
93
+ const errors = [];
94
+
95
+ // Check 1: DANGLING_EDGE_TARGET — edge targets that don't exist as nodes
96
+ const allEdges = graph.getAllEdges();
97
+ for (const edge of allEdges) {
98
+ if (!graph.getNode(edge.target)) {
99
+ errors.push({
100
+ code: 'DANGLING_EDGE_TARGET',
101
+ message: `Edge target '${edge.target}' does not exist as a node`,
102
+ details: { source: edge.source, target: edge.target }
103
+ });
104
+ }
105
+ }
106
+
107
+ // Check 2: CYCLE_DETECTED — cycles in depends_on relationships (DFS)
108
+ const dependsAdj = new Map();
109
+ for (const edge of allEdges) {
110
+ if (edge.type === 'depends_on') {
111
+ if (!dependsAdj.has(edge.source)) {
112
+ dependsAdj.set(edge.source, []);
113
+ }
114
+ dependsAdj.get(edge.source).push(edge.target);
115
+ }
116
+ }
117
+
118
+ const WHITE = 0; // unvisited
119
+ const GRAY = 1; // in current DFS path
120
+ const BLACK = 2; // fully processed
121
+ const color = new Map();
122
+ const parent = new Map();
123
+
124
+ for (const node of graph.getAllNodes()) {
125
+ color.set(node.ref, WHITE);
126
+ }
127
+
128
+ function dfs(u) {
129
+ color.set(u, GRAY);
130
+ const neighbors = dependsAdj.get(u) || [];
131
+ for (const v of neighbors) {
132
+ if (color.get(v) === GRAY) {
133
+ // Found a cycle — reconstruct the path
134
+ const cycle = [v, u];
135
+ let cur = u;
136
+ while (cur !== v) {
137
+ cur = parent.get(cur);
138
+ if (cur === undefined) break;
139
+ cycle.push(cur);
140
+ }
141
+ cycle.reverse();
142
+ if (cycle[0] !== cycle[cycle.length - 1]) {
143
+ cycle.push(cycle[0]);
144
+ }
145
+ errors.push({
146
+ code: 'CYCLE_DETECTED',
147
+ message: `Cycle detected in depends_on: ${cycle.join(' → ')}`,
148
+ details: { cycle }
149
+ });
150
+ return;
151
+ }
152
+ if (color.get(v) === WHITE) {
153
+ parent.set(v, u);
154
+ dfs(v);
155
+ }
156
+ }
157
+ color.set(u, BLACK);
158
+ }
159
+
160
+ for (const node of graph.getAllNodes()) {
161
+ if (color.get(node.ref) === WHITE) {
162
+ dfs(node.ref);
163
+ }
164
+ }
165
+
166
+ return { valid: errors.length === 0, errors };
167
+ }
168
+
169
+ /**
170
+ * 查询 ref 的完整 depends_on 依赖链(BFS 遍历)
171
+ * @param {OntologyGraph} graph
172
+ * @param {string} ref
173
+ * @returns {{ ref: string, chain: string[], hasCycle: boolean, error?: string }}
174
+ */
175
+ function queryDependencyChain(graph, ref) {
176
+ // Requirement 4.2: ref not found → return error
177
+ if (!graph.getNode(ref)) {
178
+ return { ref, chain: [], hasCycle: false, error: 'Node not found' };
179
+ }
180
+
181
+ // BFS to collect all transitive depends_on dependencies
182
+ const chain = [];
183
+ const visited = new Set();
184
+ const queue = [ref];
185
+
186
+ visited.add(ref);
187
+
188
+ while (queue.length > 0) {
189
+ const current = queue.shift();
190
+ const edges = graph.getEdges(current);
191
+
192
+ for (const edge of edges) {
193
+ if (edge.type !== 'depends_on') continue;
194
+ if (visited.has(edge.target)) continue;
195
+
196
+ visited.add(edge.target);
197
+ chain.push(edge.target);
198
+ queue.push(edge.target);
199
+ }
200
+ }
201
+
202
+ // DFS cycle detection on the depends_on subgraph reachable from ref
203
+ const WHITE = 0, GRAY = 1, BLACK = 2;
204
+ const color = new Map();
205
+ for (const node of visited) {
206
+ color.set(node, WHITE);
207
+ }
208
+
209
+ let hasCycle = false;
210
+
211
+ function dfs(u) {
212
+ if (hasCycle) return;
213
+ color.set(u, GRAY);
214
+ const edges = graph.getEdges(u);
215
+ for (const edge of edges) {
216
+ if (edge.type !== 'depends_on') continue;
217
+ if (!color.has(edge.target)) continue;
218
+ if (color.get(edge.target) === GRAY) {
219
+ hasCycle = true;
220
+ return;
221
+ }
222
+ if (color.get(edge.target) === WHITE) {
223
+ dfs(edge.target);
224
+ }
225
+ }
226
+ color.set(u, BLACK);
227
+ }
228
+
229
+ dfs(ref);
230
+
231
+ return { ref, chain, hasCycle };
232
+ }
233
+
234
+ /**
235
+ * 查询 ref 的变更影响范围(默认反向 depends_on 传播)
236
+ * @param {OntologyGraph} graph
237
+ * @param {string} ref
238
+ * @param {{ relationTypes?: string[], maxDepth?: number|null }} options
239
+ * @returns {{
240
+ * ref: string,
241
+ * relationTypes: string[],
242
+ * maxDepth: number|null,
243
+ * impacted: string[],
244
+ * details: Array<{ ref: string, depth: number, via: string, through: string }>,
245
+ * total: number,
246
+ * error?: string
247
+ * }}
248
+ */
249
+ function findImpactRadius(graph, ref, options = {}) {
250
+ if (!graph.getNode(ref)) {
251
+ return {
252
+ ref,
253
+ relationTypes: ['depends_on'],
254
+ maxDepth: null,
255
+ impacted: [],
256
+ details: [],
257
+ total: 0,
258
+ error: 'Node not found'
259
+ };
260
+ }
261
+
262
+ const relationTypes = Array.isArray(options.relationTypes) && options.relationTypes.length > 0
263
+ ? options.relationTypes
264
+ : ['depends_on'];
265
+ const invalidTypes = relationTypes.filter((type) => !VALID_RELATION_TYPES.includes(type));
266
+ if (invalidTypes.length > 0) {
267
+ return {
268
+ ref,
269
+ relationTypes,
270
+ maxDepth: null,
271
+ impacted: [],
272
+ details: [],
273
+ total: 0,
274
+ error: `Invalid relation type(s): ${invalidTypes.join(', ')}`
275
+ };
276
+ }
277
+
278
+ const maxDepth = Number.isInteger(options.maxDepth) && options.maxDepth > 0
279
+ ? options.maxDepth
280
+ : null;
281
+ const depthLimit = maxDepth === null ? Number.POSITIVE_INFINITY : maxDepth;
282
+ const relationTypeSet = new Set(relationTypes);
283
+ const incomingEdges = new Map();
284
+
285
+ for (const edge of graph.getAllEdges()) {
286
+ if (!relationTypeSet.has(edge.type)) continue;
287
+ if (!incomingEdges.has(edge.target)) {
288
+ incomingEdges.set(edge.target, []);
289
+ }
290
+ incomingEdges.get(edge.target).push(edge);
291
+ }
292
+
293
+ const visited = new Set([ref]);
294
+ const queue = [{ ref, depth: 0 }];
295
+ const impacted = [];
296
+ const details = [];
297
+
298
+ while (queue.length > 0) {
299
+ const current = queue.shift();
300
+ if (current.depth >= depthLimit) continue;
301
+
302
+ const edges = incomingEdges.get(current.ref) || [];
303
+ for (const edge of edges) {
304
+ const dependentRef = edge.source;
305
+ if (visited.has(dependentRef)) continue;
306
+
307
+ const nextDepth = current.depth + 1;
308
+ visited.add(dependentRef);
309
+ impacted.push(dependentRef);
310
+ details.push({
311
+ ref: dependentRef,
312
+ depth: nextDepth,
313
+ via: edge.type,
314
+ through: current.ref
315
+ });
316
+ queue.push({ ref: dependentRef, depth: nextDepth });
317
+ }
318
+ }
319
+
320
+ details.sort((left, right) => {
321
+ if (left.depth !== right.depth) return left.depth - right.depth;
322
+ return left.ref.localeCompare(right.ref);
323
+ });
324
+
325
+ return {
326
+ ref,
327
+ relationTypes,
328
+ maxDepth,
329
+ impacted,
330
+ details,
331
+ total: impacted.length
332
+ };
333
+ }
334
+
335
+ /**
336
+ * 查询两个节点之间的关系路径(最短路径,BFS)
337
+ * @param {OntologyGraph} graph
338
+ * @param {string} fromRef
339
+ * @param {string} toRef
340
+ * @param {{ relationTypes?: string[], undirected?: boolean }} options
341
+ * @returns {{
342
+ * from: string,
343
+ * to: string,
344
+ * relationTypes: string[],
345
+ * undirected: boolean,
346
+ * found: boolean,
347
+ * hops: number|null,
348
+ * nodes: string[],
349
+ * edges: Array<{ source: string, target: string, type: string, direction: string }>,
350
+ * error?: string
351
+ * }}
352
+ */
353
+ function findRelationPath(graph, fromRef, toRef, options = {}) {
354
+ if (!graph.getNode(fromRef)) {
355
+ return {
356
+ from: fromRef,
357
+ to: toRef,
358
+ relationTypes: VALID_RELATION_TYPES.slice(),
359
+ undirected: options.undirected === true,
360
+ found: false,
361
+ hops: null,
362
+ nodes: [],
363
+ edges: [],
364
+ error: 'Source node not found'
365
+ };
366
+ }
367
+
368
+ if (!graph.getNode(toRef)) {
369
+ return {
370
+ from: fromRef,
371
+ to: toRef,
372
+ relationTypes: VALID_RELATION_TYPES.slice(),
373
+ undirected: options.undirected === true,
374
+ found: false,
375
+ hops: null,
376
+ nodes: [],
377
+ edges: [],
378
+ error: 'Target node not found'
379
+ };
380
+ }
381
+
382
+ const relationTypes = Array.isArray(options.relationTypes) && options.relationTypes.length > 0
383
+ ? options.relationTypes
384
+ : VALID_RELATION_TYPES.slice();
385
+ const invalidTypes = relationTypes.filter((type) => !VALID_RELATION_TYPES.includes(type));
386
+ if (invalidTypes.length > 0) {
387
+ return {
388
+ from: fromRef,
389
+ to: toRef,
390
+ relationTypes,
391
+ undirected: options.undirected === true,
392
+ found: false,
393
+ hops: null,
394
+ nodes: [],
395
+ edges: [],
396
+ error: `Invalid relation type(s): ${invalidTypes.join(', ')}`
397
+ };
398
+ }
399
+
400
+ const undirected = options.undirected === true;
401
+ if (fromRef === toRef) {
402
+ return {
403
+ from: fromRef,
404
+ to: toRef,
405
+ relationTypes,
406
+ undirected,
407
+ found: true,
408
+ hops: 0,
409
+ nodes: [fromRef],
410
+ edges: []
411
+ };
412
+ }
413
+
414
+ const relationSet = new Set(relationTypes);
415
+ const outgoingEdges = new Map();
416
+ const incomingEdges = new Map();
417
+ for (const edge of graph.getAllEdges()) {
418
+ if (!relationSet.has(edge.type)) continue;
419
+ if (!outgoingEdges.has(edge.source)) {
420
+ outgoingEdges.set(edge.source, []);
421
+ }
422
+ outgoingEdges.get(edge.source).push(edge);
423
+
424
+ if (!incomingEdges.has(edge.target)) {
425
+ incomingEdges.set(edge.target, []);
426
+ }
427
+ incomingEdges.get(edge.target).push(edge);
428
+ }
429
+
430
+ const queue = [fromRef];
431
+ const visited = new Set([fromRef]);
432
+ const previous = new Map();
433
+ let found = false;
434
+
435
+ while (queue.length > 0 && !found) {
436
+ const current = queue.shift();
437
+ const currentOutgoing = outgoingEdges.get(current) || [];
438
+ const neighbors = [];
439
+
440
+ for (const edge of currentOutgoing) {
441
+ neighbors.push({
442
+ nextRef: edge.target,
443
+ traversedEdge: {
444
+ source: current,
445
+ target: edge.target,
446
+ type: edge.type,
447
+ direction: 'outgoing'
448
+ }
449
+ });
450
+ }
451
+
452
+ if (undirected) {
453
+ const currentIncoming = incomingEdges.get(current) || [];
454
+ for (const edge of currentIncoming) {
455
+ neighbors.push({
456
+ nextRef: edge.source,
457
+ traversedEdge: {
458
+ source: current,
459
+ target: edge.source,
460
+ type: edge.type,
461
+ direction: 'incoming'
462
+ }
463
+ });
464
+ }
465
+ }
466
+
467
+ for (const neighbor of neighbors) {
468
+ if (visited.has(neighbor.nextRef)) continue;
469
+ visited.add(neighbor.nextRef);
470
+ previous.set(neighbor.nextRef, {
471
+ node: current,
472
+ edge: neighbor.traversedEdge
473
+ });
474
+ queue.push(neighbor.nextRef);
475
+ if (neighbor.nextRef === toRef) {
476
+ found = true;
477
+ break;
478
+ }
479
+ }
480
+ }
481
+
482
+ if (!found) {
483
+ return {
484
+ from: fromRef,
485
+ to: toRef,
486
+ relationTypes,
487
+ undirected,
488
+ found: false,
489
+ hops: null,
490
+ nodes: [],
491
+ edges: []
492
+ };
493
+ }
494
+
495
+ const nodes = [toRef];
496
+ const edges = [];
497
+ let current = toRef;
498
+ while (current !== fromRef) {
499
+ const prev = previous.get(current);
500
+ if (!prev) break;
501
+ edges.push(prev.edge);
502
+ current = prev.node;
503
+ nodes.push(current);
504
+ }
505
+ nodes.reverse();
506
+ edges.reverse();
507
+
508
+ return {
509
+ from: fromRef,
510
+ to: toRef,
511
+ relationTypes,
512
+ undirected,
513
+ found: true,
514
+ hops: edges.length,
515
+ nodes,
516
+ edges
517
+ };
518
+ }
519
+
520
+ /**
521
+ * 从 scene-package.json 构建 OntologyGraph
522
+ * @param {Object} contract - 解析后的 scene-package.json 对象
523
+ * @returns {OntologyGraph}
524
+ */
525
+ function buildOntologyFromManifest(contract) {
526
+ const graph = new OntologyGraph();
527
+
528
+ const bindings = contract
529
+ && contract.capability_contract
530
+ && Array.isArray(contract.capability_contract.bindings)
531
+ ? contract.capability_contract.bindings
532
+ : [];
533
+
534
+ if (bindings.length === 0) {
535
+ return graph;
536
+ }
537
+
538
+ // Step 1: Create nodes for each binding
539
+ const refSet = new Set();
540
+ for (const binding of bindings) {
541
+ if (!binding.ref || typeof binding.ref !== 'string') continue;
542
+ refSet.add(binding.ref);
543
+ graph.addNode(binding.ref, {
544
+ type: binding.type || null,
545
+ timeout_ms: binding.timeout_ms || null,
546
+ intent: binding.intent || null,
547
+ preconditions: Array.isArray(binding.preconditions) ? binding.preconditions : [],
548
+ postconditions: Array.isArray(binding.postconditions) ? binding.postconditions : []
549
+ });
550
+ }
551
+
552
+ // Step 2: Infer composes relationships from shared prefixes
553
+ const prefixMap = new Map();
554
+ for (const ref of refSet) {
555
+ const segments = ref.split('.');
556
+ if (segments.length >= 2) {
557
+ const prefix = segments.slice(0, -1).join('.');
558
+ if (!prefixMap.has(prefix)) {
559
+ prefixMap.set(prefix, []);
560
+ }
561
+ prefixMap.get(prefix).push(ref);
562
+ }
563
+ }
564
+
565
+ for (const [, refs] of prefixMap) {
566
+ if (refs.length < 2) continue;
567
+ for (let i = 0; i < refs.length; i++) {
568
+ for (let j = i + 1; j < refs.length; j++) {
569
+ graph.addEdge(refs[i], refs[j], 'composes');
570
+ graph.addEdge(refs[j], refs[i], 'composes');
571
+ }
572
+ }
573
+ }
574
+
575
+ // Step 3: Parse explicit depends_on fields
576
+ for (const binding of bindings) {
577
+ if (binding.depends_on && typeof binding.depends_on === 'string' && refSet.has(binding.depends_on)) {
578
+ graph.addEdge(binding.ref, binding.depends_on, 'depends_on');
579
+ }
580
+ }
581
+
582
+ return graph;
583
+ }
584
+
585
+ /**
586
+ * 获取指定 ref 的 action abstraction 信息
587
+ * @param {OntologyGraph} graph
588
+ * @param {string} ref
589
+ * @returns {{ ref: string, intent: string|null, preconditions: string[], postconditions: string[] }}
590
+ */
591
+ function getActionInfo(graph, ref) {
592
+ const node = graph.getNode(ref);
593
+ if (!node) {
594
+ return { ref, intent: null, preconditions: [], postconditions: [] };
595
+ }
596
+ const meta = node.metadata || {};
597
+ return {
598
+ ref,
599
+ intent: typeof meta.intent === 'string' ? meta.intent : null,
600
+ preconditions: Array.isArray(meta.preconditions) ? meta.preconditions : [],
601
+ postconditions: Array.isArray(meta.postconditions) ? meta.postconditions : []
602
+ };
603
+ }
604
+
605
+ /**
606
+ * 解析 governance_contract.data_lineage
607
+ * @param {Object} contract - scene-package.json 对象
608
+ * @returns {{ sources: Array, transforms: Array, sinks: Array } | null}
609
+ */
610
+ function parseDataLineage(contract) {
611
+ const lineage = contract
612
+ && contract.governance_contract
613
+ && contract.governance_contract.data_lineage;
614
+
615
+ if (!lineage || typeof lineage !== 'object') {
616
+ return null;
617
+ }
618
+
619
+ const sources = Array.isArray(lineage.sources)
620
+ ? lineage.sources.filter(s => s && typeof s.ref === 'string' && Array.isArray(s.fields))
621
+ : [];
622
+
623
+ const transforms = Array.isArray(lineage.transforms)
624
+ ? lineage.transforms.filter(t => t && typeof t.operation === 'string')
625
+ : [];
626
+
627
+ const sinks = Array.isArray(lineage.sinks)
628
+ ? lineage.sinks.filter(s => s && typeof s.ref === 'string' && Array.isArray(s.fields))
629
+ : [];
630
+
631
+ return { sources, transforms, sinks };
632
+ }
633
+
634
+ function normalizeOntologyCollection(rawCollection) {
635
+ if (Array.isArray(rawCollection)) {
636
+ return rawCollection.filter(item => item && typeof item === 'object' && !Array.isArray(item));
637
+ }
638
+ if (!rawCollection || typeof rawCollection !== 'object' || Array.isArray(rawCollection)) {
639
+ return [];
640
+ }
641
+ const listCandidate = rawCollection.items || rawCollection.values || rawCollection.list || rawCollection.nodes;
642
+ if (Array.isArray(listCandidate)) {
643
+ return listCandidate.filter(item => item && typeof item === 'object' && !Array.isArray(item));
644
+ }
645
+ return Object.values(rawCollection)
646
+ .filter(item => item && typeof item === 'object' && !Array.isArray(item));
647
+ }
648
+
649
+ /**
650
+ * 解析实体关系模型(entities + relations)
651
+ * @param {Object} contract
652
+ * @returns {{ entities: Array, relations: Array, summary: Object }}
653
+ */
654
+ function parseEntityRelationshipModel(contract) {
655
+ const modelRoot = contract && typeof contract === 'object'
656
+ ? (
657
+ contract.ontology_model
658
+ || contract.semantic_model
659
+ || contract.domain_model
660
+ || contract
661
+ )
662
+ : {};
663
+
664
+ const entitiesRaw = normalizeOntologyCollection(
665
+ modelRoot.entities
666
+ || (modelRoot.entity_model && modelRoot.entity_model.entities)
667
+ || (modelRoot.entity_relations && modelRoot.entity_relations.entities)
668
+ );
669
+ const relationsRaw = normalizeOntologyCollection(
670
+ modelRoot.relations
671
+ || (modelRoot.entity_relations && modelRoot.entity_relations.relations)
672
+ || (modelRoot.relation_model && modelRoot.relation_model.relations)
673
+ );
674
+
675
+ const entities = entitiesRaw
676
+ .map(item => ({
677
+ ...item,
678
+ id: typeof item.id === 'string'
679
+ ? item.id
680
+ : (typeof item.ref === 'string'
681
+ ? item.ref
682
+ : (typeof item.name === 'string' ? item.name : null)),
683
+ type: typeof item.type === 'string' ? item.type : null
684
+ }))
685
+ .filter(item => typeof item.id === 'string' && item.id.length > 0);
686
+
687
+ const relations = relationsRaw
688
+ .map(item => {
689
+ const source = typeof item.source === 'string'
690
+ ? item.source
691
+ : (typeof item.from === 'string' ? item.from : null);
692
+ const target = typeof item.target === 'string'
693
+ ? item.target
694
+ : (typeof item.to === 'string' ? item.to : null);
695
+ const relationType = typeof item.type === 'string'
696
+ ? item.type
697
+ : (typeof item.relation === 'string' ? item.relation : null);
698
+ return {
699
+ ...item,
700
+ source,
701
+ target,
702
+ type: relationType
703
+ };
704
+ })
705
+ .filter(item => typeof item.source === 'string' && typeof item.target === 'string');
706
+
707
+ return {
708
+ entities,
709
+ relations,
710
+ summary: {
711
+ entity_count: entities.length,
712
+ relation_count: relations.length
713
+ }
714
+ };
715
+ }
716
+
717
+ /**
718
+ * 解析业务规则集合(business_rules)
719
+ * @param {Object} contract
720
+ * @returns {{ rules: Array, summary: Object }}
721
+ */
722
+ function parseBusinessRules(contract) {
723
+ const root = contract && typeof contract === 'object'
724
+ ? (
725
+ (contract.governance_contract && typeof contract.governance_contract === 'object'
726
+ ? contract.governance_contract
727
+ : null)
728
+ || contract.ontology_model
729
+ || contract
730
+ )
731
+ : {};
732
+
733
+ const rawRules = normalizeOntologyCollection(
734
+ root.business_rules || root.rules || root.rule_set
735
+ );
736
+
737
+ const rules = rawRules.map(item => {
738
+ const status = typeof item.status === 'string'
739
+ ? item.status.trim().toLowerCase()
740
+ : (
741
+ typeof item.state === 'string'
742
+ ? item.state.trim().toLowerCase()
743
+ : null
744
+ );
745
+ const mapped = item.mapped === true
746
+ || item.bound === true
747
+ || typeof item.entity_ref === 'string'
748
+ || typeof item.target_ref === 'string'
749
+ || typeof item.bind_to === 'string';
750
+ const passed = item.passed === true
751
+ || item.valid === true
752
+ || item.success === true
753
+ || (status ? ['passed', 'active', 'enforced', 'implemented', 'success', 'ok'].includes(status) : false);
754
+ return {
755
+ ...item,
756
+ id: typeof item.id === 'string'
757
+ ? item.id
758
+ : (typeof item.rule_id === 'string'
759
+ ? item.rule_id
760
+ : (typeof item.name === 'string' ? item.name : null)),
761
+ mapped,
762
+ passed
763
+ };
764
+ });
765
+
766
+ const total = rules.length;
767
+ const mappedCount = rules.filter(rule => rule.mapped).length;
768
+ const passedCount = rules.filter(rule => rule.passed).length;
769
+ const unmappedCount = Math.max(0, total - mappedCount);
770
+ const failedCount = Math.max(0, total - passedCount);
771
+
772
+ return {
773
+ rules,
774
+ summary: {
775
+ total,
776
+ mapped: mappedCount,
777
+ unmapped: unmappedCount,
778
+ passed: passedCount,
779
+ failed: failedCount,
780
+ mapping_rate_percent: total > 0
781
+ ? Number(((mappedCount / total) * 100).toFixed(2))
782
+ : null,
783
+ pass_rate_percent: total > 0
784
+ ? Number(((passedCount / total) * 100).toFixed(2))
785
+ : null
786
+ }
787
+ };
788
+ }
789
+
790
+ /**
791
+ * 解析决策逻辑集合(decision_logic)
792
+ * @param {Object} contract
793
+ * @returns {{ decisions: Array, summary: Object }}
794
+ */
795
+ function parseDecisionLogic(contract) {
796
+ const root = contract && typeof contract === 'object'
797
+ ? (
798
+ (contract.governance_contract && typeof contract.governance_contract === 'object'
799
+ ? contract.governance_contract
800
+ : null)
801
+ || contract.ontology_model
802
+ || contract
803
+ )
804
+ : {};
805
+
806
+ const rawDecisions = normalizeOntologyCollection(
807
+ root.decision_logic || root.decisions || root.decision_model
808
+ );
809
+
810
+ const decisions = rawDecisions.map(item => {
811
+ const status = typeof item.status === 'string'
812
+ ? item.status.trim().toLowerCase()
813
+ : (
814
+ typeof item.state === 'string'
815
+ ? item.state.trim().toLowerCase()
816
+ : null
817
+ );
818
+ const resolved = item.resolved === true
819
+ || item.decided === true
820
+ || item.applied === true
821
+ || item.completed === true
822
+ || (status ? ['resolved', 'decided', 'implemented', 'completed', 'active', 'success'].includes(status) : false);
823
+ const automated = item.automated === true || item.tested === true || item.simulated === true;
824
+ return {
825
+ ...item,
826
+ id: typeof item.id === 'string'
827
+ ? item.id
828
+ : (typeof item.decision_id === 'string'
829
+ ? item.decision_id
830
+ : (typeof item.name === 'string' ? item.name : null)),
831
+ resolved,
832
+ automated
833
+ };
834
+ });
835
+
836
+ const total = decisions.length;
837
+ const resolvedCount = decisions.filter(item => item.resolved).length;
838
+ const automatedCount = decisions.filter(item => item.automated).length;
839
+ const pendingCount = Math.max(0, total - resolvedCount);
840
+
841
+ return {
842
+ decisions,
843
+ summary: {
844
+ total,
845
+ resolved: resolvedCount,
846
+ pending: pendingCount,
847
+ undecided: pendingCount,
848
+ automated: automatedCount,
849
+ resolved_rate_percent: total > 0
850
+ ? Number(((resolvedCount / total) * 100).toFixed(2))
851
+ : null
852
+ }
853
+ };
854
+ }
855
+
856
+ /**
857
+ * 评估 ontology 语义质量分(0-100)
858
+ * @param {Object} contract
859
+ * @returns {{ score: number, level: string, components: Object, metrics: Object }}
860
+ */
861
+ function evaluateOntologySemanticQuality(contract) {
862
+ const entityModel = parseEntityRelationshipModel(contract);
863
+ const businessRules = parseBusinessRules(contract);
864
+ const decisionLogic = parseDecisionLogic(contract);
865
+
866
+ const structureScore = (
867
+ (entityModel.summary.entity_count > 0 ? 20 : 0) +
868
+ (entityModel.summary.relation_count > 0 ? 20 : 0)
869
+ );
870
+ const ruleScore = businessRules.summary.mapping_rate_percent === null
871
+ ? 15
872
+ : Number((30 * (businessRules.summary.mapping_rate_percent / 100)).toFixed(2));
873
+ const decisionScore = decisionLogic.summary.resolved_rate_percent === null
874
+ ? 15
875
+ : Number((30 * (decisionLogic.summary.resolved_rate_percent / 100)).toFixed(2));
876
+ const totalScore = Number((structureScore + ruleScore + decisionScore).toFixed(2));
877
+
878
+ let level = 'low';
879
+ if (totalScore >= 80) {
880
+ level = 'high';
881
+ } else if (totalScore >= 60) {
882
+ level = 'medium';
883
+ }
884
+
885
+ return {
886
+ score: totalScore,
887
+ level,
888
+ components: {
889
+ structure: structureScore,
890
+ business_rules: ruleScore,
891
+ decision_logic: decisionScore
892
+ },
893
+ metrics: {
894
+ entity_count: entityModel.summary.entity_count,
895
+ relation_count: entityModel.summary.relation_count,
896
+ business_rule_total: businessRules.summary.total,
897
+ business_rule_mapped: businessRules.summary.mapped,
898
+ business_rule_unmapped: businessRules.summary.unmapped,
899
+ business_rule_passed: businessRules.summary.passed,
900
+ business_rule_failed: businessRules.summary.failed,
901
+ business_rule_mapping_rate_percent: businessRules.summary.mapping_rate_percent,
902
+ business_rule_pass_rate_percent: businessRules.summary.pass_rate_percent,
903
+ decision_total: decisionLogic.summary.total,
904
+ decision_resolved: decisionLogic.summary.resolved,
905
+ decision_pending: decisionLogic.summary.pending,
906
+ decision_undecided: decisionLogic.summary.undecided,
907
+ decision_automated: decisionLogic.summary.automated,
908
+ decision_resolved_rate_percent: decisionLogic.summary.resolved_rate_percent
909
+ }
910
+ };
911
+ }
912
+
913
+ /**
914
+ * 查询指定 ref 参与的 lineage 路径
915
+ * @param {Object} contract - scene-package.json 对象
916
+ * @param {string} ref
917
+ * @returns {{ ref: string, asSource: Array, asSink: Array }}
918
+ */
919
+ function getLineageInfo(contract, ref) {
920
+ const lineage = parseDataLineage(contract);
921
+ if (!lineage) {
922
+ return { ref, asSource: [], asSink: [] };
923
+ }
924
+
925
+ const asSource = lineage.sources.filter(s => s.ref === ref);
926
+ const asSink = lineage.sinks.filter(s => s.ref === ref);
927
+
928
+ return { ref, asSource, asSink };
929
+ }
930
+
931
+ /**
932
+ * 解析 agent_hints 字段
933
+ * @param {Object} contract - scene-package.json 对象
934
+ * @returns {Object|null}
935
+ */
936
+ function getAgentHints(contract) {
937
+ if (!contract || typeof contract !== 'object') return null;
938
+ const hints = contract.agent_hints;
939
+ if (!hints || typeof hints !== 'object' || Array.isArray(hints)) return null;
940
+ return hints;
941
+ }
942
+
943
+ module.exports = {
944
+ OntologyGraph,
945
+ VALID_RELATION_TYPES,
946
+ buildOntologyFromManifest,
947
+ validateOntology,
948
+ queryDependencyChain,
949
+ findImpactRadius,
950
+ findRelationPath,
951
+ getActionInfo,
952
+ parseDataLineage,
953
+ getLineageInfo,
954
+ getAgentHints,
955
+ parseEntityRelationshipModel,
956
+ parseBusinessRules,
957
+ parseDecisionLogic,
958
+ evaluateOntologySemanticQuality
959
+ };