@documentation-robotics/cli 0.2.0 → 0.3.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 (269) hide show
  1. package/README.md +36 -2
  2. package/dist/analysis/relationship-classifier.js +2 -9
  3. package/dist/analysis/relationship-classifier.js.map +2 -2
  4. package/dist/analyzers/call-graph-utils.js +2 -2
  5. package/dist/analyzers/call-graph-utils.js.map +2 -2
  6. package/dist/analyzers/cards/build.js +171 -0
  7. package/dist/analyzers/cards/build.js.map +7 -0
  8. package/dist/analyzers/cards/classify.js +42 -0
  9. package/dist/analyzers/cards/classify.js.map +7 -0
  10. package/dist/analyzers/cards/edge-index.js +44 -0
  11. package/dist/analyzers/cards/edge-index.js.map +7 -0
  12. package/dist/analyzers/cards/facts.js +46 -0
  13. package/dist/analyzers/cards/facts.js.map +7 -0
  14. package/dist/analyzers/cards/graph.js +42 -0
  15. package/dist/analyzers/cards/graph.js.map +7 -0
  16. package/dist/analyzers/cards/index.js +8 -0
  17. package/dist/analyzers/cards/index.js.map +7 -0
  18. package/dist/analyzers/cards/tier0.js +18 -0
  19. package/dist/analyzers/cards/tier0.js.map +7 -0
  20. package/dist/analyzers/cards/types.js +1 -0
  21. package/dist/analyzers/cards/types.js.map +7 -0
  22. package/dist/analyzers/cbm-analyzer.js +195 -171
  23. package/dist/analyzers/cbm-analyzer.js.map +2 -2
  24. package/dist/analyzers/cbm-responses.js +336 -0
  25. package/dist/analyzers/cbm-responses.js.map +7 -0
  26. package/dist/analyzers/edge-relationships.js +45 -0
  27. package/dist/analyzers/edge-relationships.js.map +7 -0
  28. package/dist/analyzers/mapping-loader.js +9 -0
  29. package/dist/analyzers/mapping-loader.js.map +2 -2
  30. package/dist/analyzers/tier0-rules.js +153 -0
  31. package/dist/analyzers/tier0-rules.js.map +7 -0
  32. package/dist/analyzers/types.js.map +1 -1
  33. package/dist/audit/relationships/ai/prompts.js +10 -10
  34. package/dist/audit/relationships/ai/prompts.js.map +2 -2
  35. package/dist/audit/relationships/analysis/layer-templates.js +52 -52
  36. package/dist/audit/relationships/analysis/layer-templates.js.map +1 -1
  37. package/dist/cli.js +12 -2
  38. package/dist/cli.js.map +2 -2
  39. package/dist/coding-agents/default-permissions.js +31 -1
  40. package/dist/coding-agents/default-permissions.js.map +2 -2
  41. package/dist/coding-agents/drbot-mcp.js +167 -0
  42. package/dist/coding-agents/drbot-mcp.js.map +7 -0
  43. package/dist/commands/add.js +3 -2
  44. package/dist/commands/add.js.map +2 -2
  45. package/dist/commands/analyzer.js +117 -14
  46. package/dist/commands/analyzer.js.map +2 -2
  47. package/dist/commands/changeset-verify.js +55 -0
  48. package/dist/commands/changeset-verify.js.map +7 -0
  49. package/dist/commands/changeset.js +43 -22
  50. package/dist/commands/changeset.js.map +2 -2
  51. package/dist/commands/delete.js +10 -4
  52. package/dist/commands/delete.js.map +2 -2
  53. package/dist/commands/farm.js +176 -60
  54. package/dist/commands/farm.js.map +2 -2
  55. package/dist/commands/mcp.js +19 -4
  56. package/dist/commands/mcp.js.map +2 -2
  57. package/dist/commands/relationship.js +207 -26
  58. package/dist/commands/relationship.js.map +2 -2
  59. package/dist/commands/update.js +42 -23
  60. package/dist/commands/update.js.map +2 -2
  61. package/dist/commands/upgrade.js +99 -11
  62. package/dist/commands/upgrade.js.map +3 -3
  63. package/dist/commands/version.js +1 -1
  64. package/dist/commands/visualize.js +4 -3
  65. package/dist/commands/visualize.js.map +2 -2
  66. package/dist/core/catalog-migration.js +33 -0
  67. package/dist/core/catalog-migration.js.map +7 -0
  68. package/dist/core/changeset-analysis.js +421 -0
  69. package/dist/core/changeset-analysis.js.map +7 -0
  70. package/dist/core/changeset-base-index.js +153 -0
  71. package/dist/core/changeset-base-index.js.map +7 -0
  72. package/dist/core/changeset-brief-delta.js +102 -0
  73. package/dist/core/changeset-brief-delta.js.map +7 -0
  74. package/dist/core/changeset-graph.js +281 -0
  75. package/dist/core/changeset-graph.js.map +7 -0
  76. package/dist/core/changeset-only-guard.js +32 -0
  77. package/dist/core/changeset-only-guard.js.map +7 -0
  78. package/dist/core/changeset-summary.js +68 -0
  79. package/dist/core/changeset-summary.js.map +7 -0
  80. package/dist/core/changeset-validator.js +66 -20
  81. package/dist/core/changeset-validator.js.map +2 -2
  82. package/dist/core/changeset-verify.js +518 -0
  83. package/dist/core/changeset-verify.js.map +7 -0
  84. package/dist/core/changeset.js +56 -9
  85. package/dist/core/changeset.js.map +2 -2
  86. package/dist/core/element-type-migration.js +145 -0
  87. package/dist/core/element-type-migration.js.map +7 -0
  88. package/dist/core/farm-manifest.js +12 -6
  89. package/dist/core/farm-manifest.js.map +2 -2
  90. package/dist/core/farm-sync-engine.js +1 -1
  91. package/dist/core/farm-sync-engine.js.map +2 -2
  92. package/dist/core/farm-sync-state.js +10 -2
  93. package/dist/core/farm-sync-state.js.map +2 -2
  94. package/dist/core/migration-registry.js +75 -46
  95. package/dist/core/migration-registry.js.map +2 -2
  96. package/dist/core/model-upgrade.js +102 -0
  97. package/dist/core/model-upgrade.js.map +7 -0
  98. package/dist/core/model.js +82 -30
  99. package/dist/core/model.js.map +2 -2
  100. package/dist/core/mutation-handler.js +71 -6
  101. package/dist/core/mutation-handler.js.map +2 -2
  102. package/dist/core/relationship-migration-table.js +6639 -0
  103. package/dist/core/relationship-migration-table.js.map +7 -0
  104. package/dist/core/relationship-migration.js +272 -0
  105. package/dist/core/relationship-migration.js.map +7 -0
  106. package/dist/core/relationship-prune.js +49 -0
  107. package/dist/core/relationship-prune.js.map +7 -0
  108. package/dist/core/relationship-staging.js +68 -0
  109. package/dist/core/relationship-staging.js.map +7 -0
  110. package/dist/core/relationships.js +26 -4
  111. package/dist/core/relationships.js.map +2 -2
  112. package/dist/core/report-data-model.js +7 -13
  113. package/dist/core/report-data-model.js.map +2 -2
  114. package/dist/core/staged-changeset-storage.js +64 -17
  115. package/dist/core/staged-changeset-storage.js.map +2 -2
  116. package/dist/core/staged-source-reference.js +28 -0
  117. package/dist/core/staged-source-reference.js.map +7 -0
  118. package/dist/core/staging-area.js +83 -18
  119. package/dist/core/staging-area.js.map +2 -2
  120. package/dist/core/virtual-projection.js +13 -5
  121. package/dist/core/virtual-projection.js.map +2 -2
  122. package/dist/export/archimate-exporter.js +1 -0
  123. package/dist/export/archimate-exporter.js.map +2 -2
  124. package/dist/generated/compiled-validators.js +2 -2
  125. package/dist/generated/compiled-validators.js.map +2 -2
  126. package/dist/generated/layer-registry.js +16 -16
  127. package/dist/generated/layer-registry.js.map +2 -2
  128. package/dist/generated/node-types.js +186 -168
  129. package/dist/generated/node-types.js.map +2 -2
  130. package/dist/generated/relationship-index.js +2236 -7790
  131. package/dist/generated/relationship-index.js.map +2 -2
  132. package/dist/integrations/claude_code/agents/dr-architect.md +16 -3
  133. package/dist/integrations/claude_code/agents/dr-extractor.md +1 -1
  134. package/dist/integrations/claude_code/commands/dr-analyzer.md +565 -0
  135. package/dist/integrations/claude_code/commands/dr-map.md +13 -0
  136. package/dist/integrations/claude_code/commands/dr-plan.md +40 -0
  137. package/dist/integrations/claude_code/commands/dr-relate.md +106 -96
  138. package/dist/integrations/claude_code/commands/dr-verify.md +45 -2
  139. package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +14 -0
  140. package/dist/integrations/claude_code/docs/MCP_SERVER.md +26 -2
  141. package/dist/integrations/claude_code/docs/QUICK_START.md +37 -8
  142. package/dist/integrations/claude_code/docs/README.md +48 -1
  143. package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +93 -72
  144. package/dist/integrations/claude_code/skills/dr_02_business_layer/SKILL.md +163 -126
  145. package/dist/integrations/claude_code/skills/dr_03_product_layer/SKILL.md +161 -126
  146. package/dist/integrations/claude_code/skills/dr_04_security_layer/SKILL.md +153 -133
  147. package/dist/integrations/claude_code/skills/dr_05_application_layer/SKILL.md +129 -100
  148. package/dist/integrations/claude_code/skills/dr_06_technology_layer/SKILL.md +111 -94
  149. package/dist/integrations/claude_code/skills/dr_07_api_layer/SKILL.md +143 -157
  150. package/dist/integrations/claude_code/skills/dr_08_data_model_layer/SKILL.md +117 -33
  151. package/dist/integrations/claude_code/skills/dr_09_dataset_layer/SKILL.md +74 -25
  152. package/dist/integrations/claude_code/skills/dr_10_ux_layer/SKILL.md +88 -12
  153. package/dist/integrations/claude_code/skills/dr_11_navigation_layer/SKILL.md +83 -24
  154. package/dist/integrations/claude_code/skills/dr_12_apm_layer/SKILL.md +57 -12
  155. package/dist/integrations/claude_code/skills/dr_13_testing_layer/SKILL.md +92 -15
  156. package/dist/integrations/claude_code/skills/dr_change_planning/SKILL.md +119 -0
  157. package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +3 -0
  158. package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +1 -1
  159. package/dist/integrations/github_copilot/agents/dr-architect.md +1 -1
  160. package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +182 -102
  161. package/dist/integrations/github_copilot/skills/dr_02_business_layer/SKILL.md +266 -148
  162. package/dist/integrations/github_copilot/skills/dr_03_product_layer/SKILL.md +547 -86
  163. package/dist/integrations/github_copilot/skills/dr_04_security_layer/SKILL.md +164 -169
  164. package/dist/integrations/github_copilot/skills/dr_05_application_layer/SKILL.md +420 -150
  165. package/dist/integrations/github_copilot/skills/dr_06_technology_layer/SKILL.md +331 -138
  166. package/dist/integrations/github_copilot/skills/dr_07_api_layer/SKILL.md +265 -201
  167. package/dist/integrations/github_copilot/skills/dr_08_data_model_layer/SKILL.md +181 -58
  168. package/dist/integrations/github_copilot/skills/dr_09_dataset_layer/SKILL.md +209 -57
  169. package/dist/integrations/github_copilot/skills/dr_10_ux_layer/SKILL.md +277 -112
  170. package/dist/integrations/github_copilot/skills/dr_11_navigation_layer/SKILL.md +219 -70
  171. package/dist/integrations/github_copilot/skills/dr_12_apm_layer/SKILL.md +189 -105
  172. package/dist/integrations/github_copilot/skills/dr_13_testing_layer/SKILL.md +222 -102
  173. package/dist/integrations/github_copilot/skills/dr_change_planning/SKILL.md +119 -0
  174. package/dist/integrations/github_copilot/skills/dr_changeset_reviewer/SKILL.md +3 -0
  175. package/dist/integrations/hash-utils.js.map +2 -2
  176. package/dist/mcp/api-key-manager.js +14 -1
  177. package/dist/mcp/api-key-manager.js.map +2 -2
  178. package/dist/mcp/changeset-only.js +34 -0
  179. package/dist/mcp/changeset-only.js.map +7 -0
  180. package/dist/mcp/tool-registry.js +19 -3
  181. package/dist/mcp/tool-registry.js.map +2 -2
  182. package/dist/mcp/tools/changeset-analysis-tools.js +78 -0
  183. package/dist/mcp/tools/changeset-analysis-tools.js.map +7 -0
  184. package/dist/mcp/tools/changeset-editing.js +184 -0
  185. package/dist/mcp/tools/changeset-editing.js.map +7 -0
  186. package/dist/mcp/tools/changeset-lifecycle.js +83 -0
  187. package/dist/mcp/tools/changeset-lifecycle.js.map +7 -0
  188. package/dist/mcp/tools/changeset-shared.js +31 -0
  189. package/dist/mcp/tools/changeset-shared.js.map +7 -0
  190. package/dist/mcp/tools/changeset-show.js +17 -1
  191. package/dist/mcp/tools/changeset-show.js.map +2 -2
  192. package/dist/mcp/tools/changeset-verify.js +43 -0
  193. package/dist/mcp/tools/changeset-verify.js.map +7 -0
  194. package/dist/mcp/tools/index.js +19 -1
  195. package/dist/mcp/tools/index.js.map +2 -2
  196. package/dist/mcp/tools/model-add.js +6 -5
  197. package/dist/mcp/tools/model-add.js.map +2 -2
  198. package/dist/mcp/tools/model-delete.js +2 -2
  199. package/dist/mcp/tools/model-delete.js.map +2 -2
  200. package/dist/mcp/tools/model-search.js +14 -0
  201. package/dist/mcp/tools/model-search.js.map +2 -2
  202. package/dist/mcp/tools/model-show.js +29 -2
  203. package/dist/mcp/tools/model-show.js.map +2 -2
  204. package/dist/mcp/tools/model-trace.js +93 -30
  205. package/dist/mcp/tools/model-trace.js.map +2 -2
  206. package/dist/mcp/tools/model-update.js +35 -19
  207. package/dist/mcp/tools/model-update.js.map +2 -2
  208. package/dist/mcp/tools/relationship-tools.js +98 -0
  209. package/dist/mcp/tools/relationship-tools.js.map +7 -0
  210. package/dist/mcp/tools/shared.js +25 -2
  211. package/dist/mcp/tools/shared.js.map +2 -2
  212. package/dist/mcp/tools/spec-type.js +90 -0
  213. package/dist/mcp/tools/spec-type.js.map +7 -0
  214. package/dist/schemas/bundled/analyzers/cbm.json +1186 -68
  215. package/dist/schemas/bundled/analyzers/manifest.json +2 -2
  216. package/dist/schemas/bundled/api.json +1029 -1658
  217. package/dist/schemas/bundled/apm.json +499 -734
  218. package/dist/schemas/bundled/application.json +735 -371
  219. package/dist/schemas/bundled/base.json +3 -220
  220. package/dist/schemas/bundled/business.json +452 -366
  221. package/dist/schemas/bundled/data-model.json +395 -411
  222. package/dist/schemas/bundled/data-store.json +334 -558
  223. package/dist/schemas/bundled/manifest.json +18 -18
  224. package/dist/schemas/bundled/motivation.json +349 -368
  225. package/dist/schemas/bundled/navigation.json +415 -731
  226. package/dist/schemas/bundled/product.json +325 -159
  227. package/dist/schemas/bundled/security.json +864 -1232
  228. package/dist/schemas/bundled/technology.json +574 -703
  229. package/dist/schemas/bundled/testing.json +718 -1643
  230. package/dist/schemas/bundled/ux.json +739 -1352
  231. package/dist/server/schemas.js +8 -0
  232. package/dist/server/schemas.js.map +2 -2
  233. package/dist/server/server.js +218 -34
  234. package/dist/server/server.js.map +2 -2
  235. package/dist/server-entry.js +2 -1
  236. package/dist/server-entry.js.map +2 -2
  237. package/dist/telemetry/index.js +1 -1
  238. package/dist/utils/ansi-suppressor.js +6 -8
  239. package/dist/utils/ansi-suppressor.js.map +2 -2
  240. package/dist/utils/attribute-patch.js +41 -0
  241. package/dist/utils/attribute-patch.js.map +7 -0
  242. package/dist/utils/error-hints.js +29 -0
  243. package/dist/utils/error-hints.js.map +7 -0
  244. package/dist/utils/errors.js +9 -3
  245. package/dist/utils/errors.js.map +2 -2
  246. package/dist/utils/project-paths.js +26 -1
  247. package/dist/utils/project-paths.js.map +2 -2
  248. package/dist/utils/source-reference.js +14 -12
  249. package/dist/utils/source-reference.js.map +2 -2
  250. package/dist/utils/spec-version.js +2 -2
  251. package/dist/utils/spec-version.js.map +1 -1
  252. package/dist/validators/composed-reference-validator.js +5 -27
  253. package/dist/validators/composed-reference-validator.js.map +2 -2
  254. package/dist/validators/reference-validator.js +0 -7
  255. package/dist/validators/reference-validator.js.map +2 -2
  256. package/dist/validators/relationship-schema-validator.js +3 -3
  257. package/dist/validators/relationship-schema-validator.js.map +2 -2
  258. package/dist/validators/schema-validator.js +27 -9
  259. package/dist/validators/schema-validator.js.map +2 -2
  260. package/dist/validators/types.js.map +1 -1
  261. package/dist/viewer/README.md +6 -6
  262. package/dist/viewer/assets/main-CxzcuU_o.js +84 -0
  263. package/dist/viewer/assets/main-j-7ULYpm.css +1 -0
  264. package/dist/viewer/index.html +2 -2
  265. package/dist/viewer/manifest.json +10 -10
  266. package/dist/viewer/version.json +1 -1
  267. package/package.json +2 -1
  268. package/dist/viewer/assets/main-B7AOQXXX.js +0 -81
  269. package/dist/viewer/assets/main-vdrFrG7q.css +0 -1
@@ -2,11 +2,14 @@ import path from "path";
2
2
  import ansis from "ansis";
3
3
  import { StagedChangesetStorage } from "./staged-changeset-storage.js";
4
4
  import { BaseSnapshotManager } from "./base-snapshot-manager.js";
5
+ import { withSequenceNumbers } from "./changeset.js";
5
6
  import { ChangesetValidator } from "./changeset-validator.js";
6
7
  import { fileExists } from "../utils/file-io.js";
7
8
  import { createSha256Hash } from "../types/index.js";
8
9
  import { getErrorMessage } from "../utils/errors.js";
9
10
  import { Layer } from "./layer.js";
11
+ import { canonicalStagedSnapshot } from "./staged-source-reference.js";
12
+ import { elementIdentifiers } from "./relationships.js";
10
13
  import { isValidLayerName } from "./layers.js";
11
14
  import { isTelemetryEnabled, startSpan, startActiveSpan, endSpan, emitLog, SeverityNumber } from "../telemetry/index.js";
12
15
  import { ModelReportOrchestrator } from "../reports/model-report-orchestrator.js";
@@ -49,7 +52,7 @@ class StagingAreaManager {
49
52
  * @returns VirtualProjectionEngine instance for computing merged model views
50
53
  */
51
54
  async getProjectionEngine() {
52
- if (!this.projectionEngine) {
55
+ if (!this.projectionEngine && typeof this.model?.getVirtualProjectionEngine == "function" && (this.projectionEngine = this.model.getVirtualProjectionEngine()), !this.projectionEngine) {
53
56
  const { VirtualProjectionEngine } = await import("./virtual-projection.js");
54
57
  this.projectionEngine = new VirtualProjectionEngine(this.rootPath);
55
58
  }
@@ -62,7 +65,14 @@ class StagingAreaManager {
62
65
  if (!this.model)
63
66
  throw new Error("Model required for creating changesets");
64
67
  const id = this.generateChangesetId(), baseSnapshotId = await this.snapshotManager.captureSnapshot(this.model), changeset = await this.storage.create(id, name, description, baseSnapshotId);
65
- return changeset.id = id, changeset.markStaged(), await this.storage.save(changeset), changeset;
68
+ changeset.id = id, changeset.markStaged(), await this.storage.save(changeset);
69
+ try {
70
+ const { captureBaseIndex, writeBaseIndex } = await import("./changeset-base-index.js");
71
+ await writeBaseIndex(this.rootPath, changeset.id, await captureBaseIndex(this.model));
72
+ } catch (error) {
73
+ console.warn(`Warning: could not record the base index for changeset '${name}': ${getErrorMessage(error)}. Verify and brief will fall back to what they can tell without it.`);
74
+ }
75
+ return changeset;
66
76
  }
67
77
  /**
68
78
  * Load a changeset by name or ID
@@ -91,6 +101,30 @@ class StagingAreaManager {
91
101
  const activeChangeset = await this.getActive();
92
102
  activeChangeset && activeChangeset.id === changeset.id && await this.clearActive(), await this.storage.delete(changeset.id);
93
103
  }
104
+ /**
105
+ * Rename a changeset and/or change its description (its stated intent). Staged changes,
106
+ * status and the active marker are untouched. Names stay unique: renaming to a name another
107
+ * changeset already has is refused.
108
+ *
109
+ * @param nameOrId - Changeset ID or current name
110
+ * @param patch - Fields to change; an empty description clears it
111
+ * @throws Error if the changeset is not found, or the new name is taken
112
+ */
113
+ async update(nameOrId, patch) {
114
+ const changeset = await this.load(nameOrId);
115
+ if (!changeset || !changeset.id)
116
+ throw new Error(`Changeset '${nameOrId}' not found`);
117
+ if (patch.name !== void 0) {
118
+ const name = patch.name.trim();
119
+ if (!name)
120
+ throw new Error("Changeset name cannot be empty");
121
+ const clash = (await this.storage.list()).find((cs) => cs.name === name && cs.id !== changeset.id);
122
+ if (clash)
123
+ throw new Error(`Another changeset (${clash.id}) is already named '${name}'`);
124
+ patch = { ...patch, name };
125
+ }
126
+ return this.storage.updateMetadata(changeset.id, patch);
127
+ }
94
128
  /**
95
129
  * Stage a change in the changeset.
96
130
  * Records the change in the staging area without applying to the base model.
@@ -111,7 +145,26 @@ class StagingAreaManager {
111
145
  ...change,
112
146
  timestamp: change.timestamp || (/* @__PURE__ */ new Date()).toISOString()
113
147
  };
114
- await this.storage.addChange(changesetId, stagedChange), (await this.getProjectionEngine()).invalidateOnStage(changesetId, change.layerName);
148
+ await this.storage.addChange(changesetId, stagedChange), !change.type.startsWith("relationship-") && typeof this.model?.noteStagedLayer == "function" && this.model.noteStagedLayer(changesetId, change.layerName), (await this.getProjectionEngine()).invalidateOnStage(changesetId, change.layerName);
149
+ }
150
+ /**
151
+ * Rewrite a staged change in place, keeping its sequence number: e.g. to fold an update of an
152
+ * element into the changeset's add of it. The changeset must be in 'staged' status.
153
+ *
154
+ * @param elementId - The key (`elementId`) of the change to rewrite
155
+ * @throws Error if the changeset is not found, not staged, or has no change with that key
156
+ */
157
+ async restage(changesetId, elementId, change) {
158
+ this.validateChangesetId(changesetId);
159
+ const changeset = await this.load(changesetId);
160
+ if (!changeset)
161
+ throw new Error(`Changeset '${changesetId}' not found`);
162
+ if (changeset.status !== "staged")
163
+ throw new Error(`Cannot change staged changes on changeset with status '${changeset.status}'. Changeset must have status 'staged'.`);
164
+ await this.storage.updateChange(changesetId, elementId, {
165
+ ...change,
166
+ timestamp: change.timestamp || (/* @__PURE__ */ new Date()).toISOString()
167
+ }), (await this.getProjectionEngine()).invalidateOnStage(changesetId, change.layerName);
115
168
  }
116
169
  /**
117
170
  * Unstage a specific element from the changeset.
@@ -144,7 +197,7 @@ class StagingAreaManager {
144
197
  const changeset = await this.load(changesetId);
145
198
  if (!changeset)
146
199
  throw new Error(`Changeset '${changesetId}' not found`);
147
- changeset.changes = [], changeset.status = "discarded", changeset.updateModified(), await this.storage.save(changeset), (await this.getProjectionEngine()).invalidateOnDiscard(changesetId);
200
+ changeset.changes = [], changeset.unstaged = [], changeset.status = "discarded", changeset.updateModified(), await this.storage.save(changeset), (await this.getProjectionEngine()).invalidateOnDiscard(changesetId);
148
201
  }
149
202
  /**
150
203
  * Capture a base snapshot of the current model state for drift detection.
@@ -251,21 +304,32 @@ class StagingAreaManager {
251
304
  failed: 0,
252
305
  validation: {
253
306
  passed: !0,
254
- errors: []
307
+ errors: [],
308
+ ran: shouldValidate
255
309
  }
256
310
  };
257
311
  if (shouldValidate && await startActiveSpan("staging.validate", async (validationSpan) => {
258
- const validation = await this.validator.validateChangeset(model, changesetId);
259
- if (validationSpan.setAttribute("validation.passed", validation.isValid()), validationSpan.setAttribute("validation.errorCount", validation.errors.length), validationSpan.setStatus({ code: 0 }), !validation.isValid())
260
- throw result.validation.passed = !1, result.validation.errors = validation.errors.map((e) => e.message), emitLog(SeverityNumber.ERROR, "Validation failed", {
312
+ const report = await this.validator.validateChangesetReport(model, changesetId), validation = report.result;
313
+ if (result.validation.preExistingErrors = report.preExistingErrors.length, report.preExistingErrors.length > 0 && (result.validation.preExistingSample = report.preExistingErrors.slice(0, 10).map((e) => e.message), emitLog(SeverityNumber.WARN, "Pre-existing validation errors not counted against the changeset", {
314
+ "validation.preExistingErrorCount": report.preExistingErrors.length
315
+ })), report.baseValidationFailure && (result.validation.baseValidationFailure = report.baseValidationFailure, emitLog(SeverityNumber.WARN, "Base model could not be validated; every error counts", {
316
+ "validation.baseValidationFailure": report.baseValidationFailure
317
+ })), validationSpan.setAttribute("validation.passed", validation.isValid()), validationSpan.setAttribute("validation.errorCount", validation.errors.length), validationSpan.setAttribute("validation.preExistingErrorCount", report.preExistingErrors.length), validationSpan.setStatus({ code: 0 }), !validation.isValid()) {
318
+ result.validation.passed = !1, result.validation.errors = validation.errors.map((e) => e.message), emitLog(SeverityNumber.ERROR, "Validation failed", {
261
319
  "validation.errorCount": result.validation.errors.length,
262
320
  "validation.errors": result.validation.errors.slice(0, 10)
263
321
  // First 10 errors
264
- }), new Error(`Validation failed with ${result.validation.errors.length} error(s):
322
+ });
323
+ const notes = [];
324
+ throw report.preExistingErrors.length > 0 && notes.push(`${report.preExistingErrors.length} pre-existing error(s) in the base model were not counted against this changeset.`), report.baseValidationFailure && notes.push(`The base model could not be validated (${report.baseValidationFailure}), so every error was counted.`), new Error(`Validation failed with ${result.validation.errors.length} error(s) introduced by this changeset:
265
325
  ` + result.validation.errors.map((e) => ` - ${e}`).join(`
266
- `) + `
326
+ `) + (notes.length > 0 ? `
327
+
328
+ ${notes.join(`
329
+ `)}` : "") + `
267
330
 
268
- Fix these validation errors before committing.`);
331
+ Fix these validation errors before committing, or skip validation with --no-validate.`);
332
+ }
269
333
  }), isDryRun)
270
334
  return result.committed = changeset.changes.length, drift.isDrifted && (result.driftWarning = drift), result;
271
335
  let backup;
@@ -274,7 +338,8 @@ Fix these validation errors before committing.`);
274
338
  } catch (backupError) {
275
339
  throw new Error(`Failed to create backup before commit. No changes have been applied. ${backupError instanceof Error ? backupError.message : String(backupError)}`);
276
340
  }
277
- const sortedChanges = [...changeset.changes].sort((a, b) => a.sequenceNumber - b.sequenceNumber);
341
+ const sortedChanges = withSequenceNumbers(changeset.changes).sort((a, b) => a.sequenceNumber - b.sequenceNumber);
342
+ let relationshipsPurged = !1;
278
343
  try {
279
344
  const applySpan = isTelemetryEnabled ? startSpan("staging.apply", {
280
345
  "apply.changeCount": changeset.changes.length
@@ -309,19 +374,19 @@ Fix these validation errors before committing.`);
309
374
  "change.layer": change.layerName
310
375
  });
311
376
  else {
312
- const element = new Element(elementData);
377
+ const element = new Element(canonicalStagedSnapshot(elementData).data);
313
378
  layer.addElement(element), result.committed++;
314
379
  }
315
380
  } else if (change.type === "update" && change.after) {
316
381
  const element = layer.getElement(change.elementId);
317
382
  if (element) {
318
- const elementData = change.after;
319
- Object.assign(element, elementData), layer.updateElement(element), result.committed++;
383
+ const { data: elementData, sourceReference } = canonicalStagedSnapshot(change.after);
384
+ Object.assign(element, elementData), sourceReference.present && element.setSourceReference(sourceReference.value), layer.updateElement(element), result.committed++;
320
385
  } else
321
386
  result.failed++, result.validation.passed = !1, result.validation.errors.push(`Cannot update '${change.elementId}' in '${change.layerName}': element not found in base model`);
322
387
  } else if (change.type === "delete") {
323
- const element = layer.getElement(change.elementId);
324
- element ? (layer.deleteElement(element.path || element.id), result.committed++) : (result.skipped = (result.skipped ?? 0) + 1, result.skippedDetails = result.skippedDetails ?? [], result.skippedDetails.push(`'${change.elementId}' in '${change.layerName}' not found \u2014 already deleted`));
388
+ const element = layer.getElement(change.elementId), before = change.before ?? {};
389
+ model.relationships.deleteForElement(elementIdentifiers(element ?? before, before.id, before.path, change.elementId)) > 0 && (relationshipsPurged = !0), element ? (layer.deleteElement(element.path || element.id), result.committed++) : (result.skipped = (result.skipped ?? 0) + 1, result.skippedDetails = result.skippedDetails ?? [], result.skippedDetails.push(`'${change.elementId}' in '${change.layerName}' not found \u2014 already deleted`));
325
390
  } else
326
391
  result.failed++, result.validation.passed = !1, result.validation.errors.push(`Cannot apply change '${change.type}' for '${change.elementId}': missing payload or unrecognised type`);
327
392
  } catch (error) {
@@ -346,7 +411,7 @@ Fix these validation errors before committing.`);
346
411
  } catch (error) {
347
412
  throw new Error(`Failed to save layer '${layerName}': ${getErrorMessage(error)}`);
348
413
  }
349
- if (hasRelationshipChanges)
414
+ if (hasRelationshipChanges || relationshipsPurged)
350
415
  try {
351
416
  await model.saveRelationships();
352
417
  } catch (error) {