@ktpartners/dgs-platform 2.6.2

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 (256) hide show
  1. package/LICENSE +38 -0
  2. package/README.md +851 -0
  3. package/agents/dgs-codebase-cross-analyzer.md +183 -0
  4. package/agents/dgs-codebase-mapper.md +782 -0
  5. package/agents/dgs-codebase-synthesizer.md +156 -0
  6. package/agents/dgs-debugger.md +1256 -0
  7. package/agents/dgs-executor.md +550 -0
  8. package/agents/dgs-integration-checker.md +481 -0
  9. package/agents/dgs-nyquist-auditor.md +178 -0
  10. package/agents/dgs-phase-researcher.md +563 -0
  11. package/agents/dgs-phase-verifier.md +450 -0
  12. package/agents/dgs-plan-checker.md +708 -0
  13. package/agents/dgs-planner.md +1324 -0
  14. package/agents/dgs-project-researcher.md +631 -0
  15. package/agents/dgs-research-synthesizer.md +249 -0
  16. package/agents/dgs-roadmapper.md +652 -0
  17. package/agents/dgs-verifier.md +607 -0
  18. package/bin/install.js +2073 -0
  19. package/commands/dgs/add-doc.md +45 -0
  20. package/commands/dgs/add-idea.md +38 -0
  21. package/commands/dgs/add-phase.md +43 -0
  22. package/commands/dgs/add-repo.md +54 -0
  23. package/commands/dgs/add-tests.md +41 -0
  24. package/commands/dgs/add-todo.md +47 -0
  25. package/commands/dgs/approve-spec.md +38 -0
  26. package/commands/dgs/audit-milestone.md +36 -0
  27. package/commands/dgs/audit-phase.md +37 -0
  28. package/commands/dgs/cancel-job.md +23 -0
  29. package/commands/dgs/capture-principle.md +143 -0
  30. package/commands/dgs/check-todos.md +45 -0
  31. package/commands/dgs/cleanup.md +18 -0
  32. package/commands/dgs/complete-milestone.md +136 -0
  33. package/commands/dgs/complete-project.md +70 -0
  34. package/commands/dgs/consolidate-ideas.md +50 -0
  35. package/commands/dgs/create-milestone-job.md +37 -0
  36. package/commands/dgs/debug.md +164 -0
  37. package/commands/dgs/develop-idea.md +53 -0
  38. package/commands/dgs/discuss-idea.md +41 -0
  39. package/commands/dgs/discuss-phase.md +83 -0
  40. package/commands/dgs/execute-phase.md +41 -0
  41. package/commands/dgs/fast.md +38 -0
  42. package/commands/dgs/find-related-ideas.md +43 -0
  43. package/commands/dgs/health.md +28 -0
  44. package/commands/dgs/help.md +22 -0
  45. package/commands/dgs/import-spec.md +36 -0
  46. package/commands/dgs/init-product.md +28 -0
  47. package/commands/dgs/insert-phase.md +32 -0
  48. package/commands/dgs/join-discord.md +18 -0
  49. package/commands/dgs/list-docs.md +40 -0
  50. package/commands/dgs/list-ideas.md +42 -0
  51. package/commands/dgs/list-jobs.md +22 -0
  52. package/commands/dgs/list-phase-assumptions.md +46 -0
  53. package/commands/dgs/list-projects.md +57 -0
  54. package/commands/dgs/list-specs.md +40 -0
  55. package/commands/dgs/map-codebase.md +92 -0
  56. package/commands/dgs/new-milestone.md +44 -0
  57. package/commands/dgs/new-project.md +42 -0
  58. package/commands/dgs/node-repair.md +26 -0
  59. package/commands/dgs/overlap-check.md +20 -0
  60. package/commands/dgs/pause-work.md +38 -0
  61. package/commands/dgs/plan-milestone-gaps.md +34 -0
  62. package/commands/dgs/plan-phase.md +44 -0
  63. package/commands/dgs/progress.md +24 -0
  64. package/commands/dgs/quick.md +41 -0
  65. package/commands/dgs/reactivate-project.md +70 -0
  66. package/commands/dgs/reapply-patches.md +110 -0
  67. package/commands/dgs/refine-spec.md +38 -0
  68. package/commands/dgs/reject-idea.md +43 -0
  69. package/commands/dgs/remove-doc.md +44 -0
  70. package/commands/dgs/remove-phase.md +31 -0
  71. package/commands/dgs/remove-repo.md +69 -0
  72. package/commands/dgs/research-idea.md +43 -0
  73. package/commands/dgs/research-phase.md +189 -0
  74. package/commands/dgs/restore-idea.md +45 -0
  75. package/commands/dgs/resume-work.md +40 -0
  76. package/commands/dgs/rollback-job.md +24 -0
  77. package/commands/dgs/run-job.md +35 -0
  78. package/commands/dgs/search.md +40 -0
  79. package/commands/dgs/set-profile.md +34 -0
  80. package/commands/dgs/settings.md +38 -0
  81. package/commands/dgs/switch-project.md +58 -0
  82. package/commands/dgs/undo-consolidation.md +42 -0
  83. package/commands/dgs/update-idea.md +44 -0
  84. package/commands/dgs/update.md +37 -0
  85. package/commands/dgs/validate-phase.md +35 -0
  86. package/commands/dgs/verify-work.md +39 -0
  87. package/commands/dgs/write-spec.md +49 -0
  88. package/deliver-great-systems/.planning/phases/09-backend-wiring-and-error-handling/09-01-SUMMARY.md +84 -0
  89. package/deliver-great-systems/.planning/phases/09-backend-wiring-and-error-handling/09-02-SUMMARY.md +86 -0
  90. package/deliver-great-systems/.planning/phases/10-v1-to-v2-migration-flow/10-01-SUMMARY.md +85 -0
  91. package/deliver-great-systems/bin/dgs-tools.cjs +1444 -0
  92. package/deliver-great-systems/bin/lib/auto-test.cjs +1365 -0
  93. package/deliver-great-systems/bin/lib/commands.cjs +570 -0
  94. package/deliver-great-systems/bin/lib/config.cjs +417 -0
  95. package/deliver-great-systems/bin/lib/conflict-agent.cjs +1063 -0
  96. package/deliver-great-systems/bin/lib/conflict-agent.test.cjs +554 -0
  97. package/deliver-great-systems/bin/lib/context.cjs +929 -0
  98. package/deliver-great-systems/bin/lib/context.test.cjs +693 -0
  99. package/deliver-great-systems/bin/lib/core.cjs +744 -0
  100. package/deliver-great-systems/bin/lib/core.test.cjs +822 -0
  101. package/deliver-great-systems/bin/lib/docs.cjs +919 -0
  102. package/deliver-great-systems/bin/lib/docs.test.cjs +211 -0
  103. package/deliver-great-systems/bin/lib/execution.cjs +705 -0
  104. package/deliver-great-systems/bin/lib/execution.test.cjs +1472 -0
  105. package/deliver-great-systems/bin/lib/frontmatter.cjs +324 -0
  106. package/deliver-great-systems/bin/lib/ideas.cjs +1406 -0
  107. package/deliver-great-systems/bin/lib/ideas.test.cjs +1417 -0
  108. package/deliver-great-systems/bin/lib/identity.cjs +125 -0
  109. package/deliver-great-systems/bin/lib/init.cjs +1114 -0
  110. package/deliver-great-systems/bin/lib/init.test.cjs +1271 -0
  111. package/deliver-great-systems/bin/lib/jobs.cjs +2015 -0
  112. package/deliver-great-systems/bin/lib/jobs.test.cjs +2619 -0
  113. package/deliver-great-systems/bin/lib/merge-conflicts.cjs +654 -0
  114. package/deliver-great-systems/bin/lib/merge-conflicts.test.cjs +370 -0
  115. package/deliver-great-systems/bin/lib/migration.cjs +352 -0
  116. package/deliver-great-systems/bin/lib/migration.test.cjs +582 -0
  117. package/deliver-great-systems/bin/lib/milestone.cjs +243 -0
  118. package/deliver-great-systems/bin/lib/overlap.cjs +437 -0
  119. package/deliver-great-systems/bin/lib/overlap.test.cjs +747 -0
  120. package/deliver-great-systems/bin/lib/path-audit.test.cjs +384 -0
  121. package/deliver-great-systems/bin/lib/paths.cjs +144 -0
  122. package/deliver-great-systems/bin/lib/paths.test.cjs +486 -0
  123. package/deliver-great-systems/bin/lib/phase.cjs +910 -0
  124. package/deliver-great-systems/bin/lib/projects.cjs +691 -0
  125. package/deliver-great-systems/bin/lib/projects.test.cjs +871 -0
  126. package/deliver-great-systems/bin/lib/repos.cjs +1432 -0
  127. package/deliver-great-systems/bin/lib/repos.test.cjs +1882 -0
  128. package/deliver-great-systems/bin/lib/roadmap.cjs +305 -0
  129. package/deliver-great-systems/bin/lib/search.cjs +570 -0
  130. package/deliver-great-systems/bin/lib/specs.cjs +1303 -0
  131. package/deliver-great-systems/bin/lib/state.cjs +893 -0
  132. package/deliver-great-systems/bin/lib/template.cjs +228 -0
  133. package/deliver-great-systems/bin/lib/test-helpers.cjs +291 -0
  134. package/deliver-great-systems/bin/lib/verify.cjs +796 -0
  135. package/deliver-great-systems/references/checkpoints.md +776 -0
  136. package/deliver-great-systems/references/conflict-resolution.md +66 -0
  137. package/deliver-great-systems/references/context-tiers.md +166 -0
  138. package/deliver-great-systems/references/continuation-format.md +249 -0
  139. package/deliver-great-systems/references/decimal-phase-calculation.md +67 -0
  140. package/deliver-great-systems/references/git-integration.md +250 -0
  141. package/deliver-great-systems/references/git-planning-commit.md +40 -0
  142. package/deliver-great-systems/references/model-profile-resolution.md +36 -0
  143. package/deliver-great-systems/references/model-profiles.md +95 -0
  144. package/deliver-great-systems/references/phase-argument-parsing.md +61 -0
  145. package/deliver-great-systems/references/planning-config.md +224 -0
  146. package/deliver-great-systems/references/questioning.md +162 -0
  147. package/deliver-great-systems/references/spec-review-loop.md +177 -0
  148. package/deliver-great-systems/references/tdd.md +265 -0
  149. package/deliver-great-systems/references/ui-brand.md +160 -0
  150. package/deliver-great-systems/references/verification-patterns.md +612 -0
  151. package/deliver-great-systems/templates/DEBUG.md +166 -0
  152. package/deliver-great-systems/templates/UAT.md +251 -0
  153. package/deliver-great-systems/templates/VALIDATION.md +95 -0
  154. package/deliver-great-systems/templates/claude-md.md +74 -0
  155. package/deliver-great-systems/templates/codebase/architecture.md +257 -0
  156. package/deliver-great-systems/templates/codebase/concerns.md +312 -0
  157. package/deliver-great-systems/templates/codebase/conventions.md +309 -0
  158. package/deliver-great-systems/templates/codebase/integrations.md +282 -0
  159. package/deliver-great-systems/templates/codebase/stack.md +188 -0
  160. package/deliver-great-systems/templates/codebase/structure.md +287 -0
  161. package/deliver-great-systems/templates/codebase/testing.md +482 -0
  162. package/deliver-great-systems/templates/config.json +38 -0
  163. package/deliver-great-systems/templates/context.md +354 -0
  164. package/deliver-great-systems/templates/continue-here.md +80 -0
  165. package/deliver-great-systems/templates/debug-subagent-prompt.md +93 -0
  166. package/deliver-great-systems/templates/discovery.md +148 -0
  167. package/deliver-great-systems/templates/milestone-archive.md +125 -0
  168. package/deliver-great-systems/templates/milestone.md +117 -0
  169. package/deliver-great-systems/templates/phase-prompt.md +615 -0
  170. package/deliver-great-systems/templates/planner-subagent-prompt.md +119 -0
  171. package/deliver-great-systems/templates/project.md +186 -0
  172. package/deliver-great-systems/templates/requirements.md +233 -0
  173. package/deliver-great-systems/templates/research-project/ARCHITECTURE.md +206 -0
  174. package/deliver-great-systems/templates/research-project/FEATURES.md +149 -0
  175. package/deliver-great-systems/templates/research-project/PITFALLS.md +202 -0
  176. package/deliver-great-systems/templates/research-project/STACK.md +122 -0
  177. package/deliver-great-systems/templates/research-project/SUMMARY.md +172 -0
  178. package/deliver-great-systems/templates/research.md +554 -0
  179. package/deliver-great-systems/templates/retrospective.md +54 -0
  180. package/deliver-great-systems/templates/roadmap.md +204 -0
  181. package/deliver-great-systems/templates/state.md +178 -0
  182. package/deliver-great-systems/templates/summary-complex.md +59 -0
  183. package/deliver-great-systems/templates/summary-minimal.md +41 -0
  184. package/deliver-great-systems/templates/summary-standard.md +48 -0
  185. package/deliver-great-systems/templates/summary.md +253 -0
  186. package/deliver-great-systems/templates/user-setup.md +313 -0
  187. package/deliver-great-systems/templates/verification-report.md +324 -0
  188. package/deliver-great-systems/workflows/add-doc.md +151 -0
  189. package/deliver-great-systems/workflows/add-idea.md +96 -0
  190. package/deliver-great-systems/workflows/add-phase.md +120 -0
  191. package/deliver-great-systems/workflows/add-tests.md +359 -0
  192. package/deliver-great-systems/workflows/add-todo.md +162 -0
  193. package/deliver-great-systems/workflows/approve-spec.md +194 -0
  194. package/deliver-great-systems/workflows/audit-milestone.md +364 -0
  195. package/deliver-great-systems/workflows/audit-phase.md +462 -0
  196. package/deliver-great-systems/workflows/cancel-job.md +108 -0
  197. package/deliver-great-systems/workflows/check-todos.md +181 -0
  198. package/deliver-great-systems/workflows/cleanup.md +247 -0
  199. package/deliver-great-systems/workflows/codereview.md +526 -0
  200. package/deliver-great-systems/workflows/complete-milestone.md +1298 -0
  201. package/deliver-great-systems/workflows/consolidate-ideas.md +365 -0
  202. package/deliver-great-systems/workflows/create-milestone-job.md +177 -0
  203. package/deliver-great-systems/workflows/develop-idea.md +544 -0
  204. package/deliver-great-systems/workflows/diagnose-issues.md +231 -0
  205. package/deliver-great-systems/workflows/discovery-phase.md +301 -0
  206. package/deliver-great-systems/workflows/discuss-idea.md +263 -0
  207. package/deliver-great-systems/workflows/discuss-phase.md +733 -0
  208. package/deliver-great-systems/workflows/execute-phase.md +571 -0
  209. package/deliver-great-systems/workflows/execute-plan.md +592 -0
  210. package/deliver-great-systems/workflows/find-related-ideas.md +271 -0
  211. package/deliver-great-systems/workflows/health.md +173 -0
  212. package/deliver-great-systems/workflows/help.md +997 -0
  213. package/deliver-great-systems/workflows/import-spec.md +381 -0
  214. package/deliver-great-systems/workflows/init-product.md +767 -0
  215. package/deliver-great-systems/workflows/insert-phase.md +138 -0
  216. package/deliver-great-systems/workflows/list-docs.md +119 -0
  217. package/deliver-great-systems/workflows/list-ideas.md +154 -0
  218. package/deliver-great-systems/workflows/list-jobs.md +89 -0
  219. package/deliver-great-systems/workflows/list-phase-assumptions.md +192 -0
  220. package/deliver-great-systems/workflows/list-specs.md +101 -0
  221. package/deliver-great-systems/workflows/map-codebase.md +621 -0
  222. package/deliver-great-systems/workflows/new-milestone.md +591 -0
  223. package/deliver-great-systems/workflows/new-project.md +1113 -0
  224. package/deliver-great-systems/workflows/node-repair.md +94 -0
  225. package/deliver-great-systems/workflows/overlap-check.md +86 -0
  226. package/deliver-great-systems/workflows/pause-work.md +134 -0
  227. package/deliver-great-systems/workflows/plan-milestone-gaps.md +306 -0
  228. package/deliver-great-systems/workflows/plan-phase.md +698 -0
  229. package/deliver-great-systems/workflows/progress.md +386 -0
  230. package/deliver-great-systems/workflows/quick.md +845 -0
  231. package/deliver-great-systems/workflows/refine-spec.md +275 -0
  232. package/deliver-great-systems/workflows/reject-idea.md +109 -0
  233. package/deliver-great-systems/workflows/remove-doc.md +117 -0
  234. package/deliver-great-systems/workflows/remove-phase.md +163 -0
  235. package/deliver-great-systems/workflows/research-idea.md +325 -0
  236. package/deliver-great-systems/workflows/research-phase.md +81 -0
  237. package/deliver-great-systems/workflows/restore-idea.md +101 -0
  238. package/deliver-great-systems/workflows/resume-project.md +311 -0
  239. package/deliver-great-systems/workflows/rollback-job.md +130 -0
  240. package/deliver-great-systems/workflows/run-job.md +498 -0
  241. package/deliver-great-systems/workflows/search.md +130 -0
  242. package/deliver-great-systems/workflows/set-profile.md +83 -0
  243. package/deliver-great-systems/workflows/settings.md +470 -0
  244. package/deliver-great-systems/workflows/transition.md +563 -0
  245. package/deliver-great-systems/workflows/undo-consolidation.md +155 -0
  246. package/deliver-great-systems/workflows/update-idea.md +157 -0
  247. package/deliver-great-systems/workflows/update.md +242 -0
  248. package/deliver-great-systems/workflows/validate-phase.md +177 -0
  249. package/deliver-great-systems/workflows/verify-phase.md +253 -0
  250. package/deliver-great-systems/workflows/verify-work.md +671 -0
  251. package/deliver-great-systems/workflows/write-spec.md +450 -0
  252. package/hooks/dist/dgs-check-update.js +62 -0
  253. package/hooks/dist/dgs-context-monitor.js +141 -0
  254. package/hooks/dist/dgs-statusline.js +115 -0
  255. package/package.json +60 -0
  256. package/scripts/build-hooks.js +43 -0
@@ -0,0 +1,554 @@
1
+ /**
2
+ * Tests for conflict-agent.cjs pure functions
3
+ *
4
+ * Covers resolution strategy selection (ADDITIVE, DELETION, STRUCTURAL, DIVERGENT),
5
+ * whole-file resolution, semantic conflict detection, cascading learning context,
6
+ * and resolution report generation.
7
+ *
8
+ * Uses Node.js built-in test runner (node:test) and assert (node:assert/strict).
9
+ */
10
+
11
+ const { describe, it } = require('node:test');
12
+ const assert = require('node:assert/strict');
13
+
14
+ const {
15
+ selectResolutionStrategy,
16
+ resolveFileContent,
17
+ detectSemanticConflicts,
18
+ createLearningContext,
19
+ mergeLearningContexts,
20
+ buildResolutionReport,
21
+ } = require('./conflict-agent.cjs');
22
+
23
+ // ─── Helper ──────────────────────────────────────────────────────────────────
24
+
25
+ /**
26
+ * Build a file content string with one or more conflict hunks.
27
+ * @param {Array<{ours: string, theirs: string}>} hunks
28
+ * @returns {string}
29
+ */
30
+ function buildConflictFile(hunks) {
31
+ const parts = ['// preamble'];
32
+ for (const h of hunks) {
33
+ parts.push('<<<<<<< HEAD');
34
+ if (h.ours) parts.push(h.ours);
35
+ parts.push('=======');
36
+ if (h.theirs) parts.push(h.theirs);
37
+ parts.push('>>>>>>> feature-branch');
38
+ }
39
+ parts.push('// postamble');
40
+ return parts.join('\n');
41
+ }
42
+
43
+ // ─── selectResolutionStrategy ────────────────────────────────────────────────
44
+
45
+ describe('selectResolutionStrategy', () => {
46
+
47
+ it('selects theirs for ADDITIVE hunk where ours is empty and theirs has content', () => {
48
+ const hunks = [{
49
+ type: 'ADDITIVE',
50
+ confidence: 'HIGH',
51
+ ours: '',
52
+ theirs: 'const newFeature = true;',
53
+ start_line: 2,
54
+ end_line: 6,
55
+ }];
56
+ const result = selectResolutionStrategy(hunks, null, null);
57
+ assert.equal(result.length, 1);
58
+ assert.equal(result[0].strategy, 'theirs');
59
+ assert.equal(result[0].confidence, 'HIGH');
60
+ });
61
+
62
+ it('selects ours for ADDITIVE hunk where theirs is empty and ours has content', () => {
63
+ const hunks = [{
64
+ type: 'ADDITIVE',
65
+ confidence: 'HIGH',
66
+ ours: 'const added = true;',
67
+ theirs: '',
68
+ start_line: 2,
69
+ end_line: 6,
70
+ }];
71
+ const result = selectResolutionStrategy(hunks, null, null);
72
+ assert.equal(result.length, 1);
73
+ assert.equal(result[0].strategy, 'ours');
74
+ assert.equal(result[0].confidence, 'HIGH');
75
+ });
76
+
77
+ it('selects theirs for ADDITIVE hunk where theirs adds new content (superset)', () => {
78
+ const hunks = [{
79
+ type: 'ADDITIVE',
80
+ confidence: 'MEDIUM',
81
+ ours: 'const a = 1;',
82
+ theirs: 'const a = 1;\nconst b = 2;',
83
+ start_line: 2,
84
+ end_line: 8,
85
+ }];
86
+ const result = selectResolutionStrategy(hunks, null, null);
87
+ assert.equal(result.length, 1);
88
+ assert.equal(result[0].strategy, 'theirs');
89
+ assert.equal(result[0].confidence, 'HIGH');
90
+ });
91
+
92
+ it('selects combined for STRUCTURAL hunk with different imports on each side', () => {
93
+ const hunks = [{
94
+ type: 'STRUCTURAL',
95
+ confidence: 'HIGH',
96
+ ours: "const utils = require('./utils.cjs');",
97
+ theirs: "const helpers = require('./helpers.cjs');",
98
+ start_line: 1,
99
+ end_line: 5,
100
+ }];
101
+ const result = selectResolutionStrategy(hunks, null, null);
102
+ assert.equal(result.length, 1);
103
+ assert.equal(result[0].strategy, 'combined');
104
+ assert.ok(result[0].confidence === 'HIGH' || result[0].confidence === 'MEDIUM');
105
+ });
106
+
107
+ it('escalates DIVERGENT hunk without plan context', () => {
108
+ const hunks = [{
109
+ type: 'DIVERGENT',
110
+ confidence: 'LOW',
111
+ ours: 'const mode = "dark";',
112
+ theirs: 'const mode = "light";',
113
+ start_line: 3,
114
+ end_line: 7,
115
+ }];
116
+ const result = selectResolutionStrategy(hunks, null, null);
117
+ assert.equal(result.length, 1);
118
+ assert.equal(result[0].strategy, 'escalate');
119
+ assert.equal(result[0].confidence, 'LOW');
120
+ });
121
+
122
+ it('selects combined for DIVERGENT hunk when plan context is available and no overlapping lines', () => {
123
+ const hunks = [{
124
+ type: 'DIVERGENT',
125
+ confidence: 'LOW',
126
+ ours: 'const timeout = 3000;',
127
+ theirs: 'const retries = 5;',
128
+ start_line: 3,
129
+ end_line: 7,
130
+ }];
131
+ const planContext = [
132
+ { task_name: 'Update config', action_excerpt: 'adjust settings for both timeout and retries' },
133
+ ];
134
+ const result = selectResolutionStrategy(hunks, planContext, null);
135
+ assert.equal(result.length, 1);
136
+ assert.equal(result[0].strategy, 'combined');
137
+ assert.equal(result[0].confidence, 'MEDIUM');
138
+ });
139
+
140
+ it('selects ours (keep-content) for DELETION hunk by default', () => {
141
+ const hunks = [{
142
+ type: 'DELETION',
143
+ confidence: 'HIGH',
144
+ ours: 'const legacy = true;',
145
+ theirs: '',
146
+ start_line: 2,
147
+ end_line: 6,
148
+ }];
149
+ const result = selectResolutionStrategy(hunks, null, null);
150
+ assert.equal(result.length, 1);
151
+ assert.equal(result[0].strategy, 'ours');
152
+ assert.equal(result[0].confidence, 'MEDIUM');
153
+ assert.ok(result[0].reasoning.includes('keeping content'));
154
+ });
155
+
156
+ it('boosts confidence when accumulated learnings match domain', () => {
157
+ const hunks = [{
158
+ type: 'DIVERGENT',
159
+ confidence: 'LOW',
160
+ ours: 'const timeout = 3000;',
161
+ theirs: 'const retries = 5;',
162
+ start_line: 3,
163
+ end_line: 7,
164
+ }];
165
+ const planContext = [
166
+ { task_name: 'Update config', action_excerpt: 'Adjust timeout and retries' },
167
+ ];
168
+ const learnings = {
169
+ files_resolved: ['config.cjs'],
170
+ strategies_used: [{ file: 'config.cjs', strategy: 'combined' }],
171
+ domain_changes: { config: 'Resolved via combined' },
172
+ phase_merges_completed: 1,
173
+ };
174
+ // DIVERGENT with plan context + no overlap => 'combined' at MEDIUM
175
+ // Learnings have 'config' domain, and hunk text doesn't necessarily contain 'config'
176
+ // But timeout/retries text may not match 'config' domain, so let's use matching domain
177
+ const hunks2 = [{
178
+ type: 'DIVERGENT',
179
+ confidence: 'LOW',
180
+ ours: 'const config = { timeout: 3000 };',
181
+ theirs: 'const config = { retries: 5 };',
182
+ start_line: 3,
183
+ end_line: 7,
184
+ }];
185
+ const result = selectResolutionStrategy(hunks2, planContext, learnings);
186
+ assert.equal(result.length, 1);
187
+ // DIVERGENT with context and overlapping 'config' line => escalate because overlap
188
+ // Let's use non-overlapping lines that contain 'config' in their text
189
+ const hunks3 = [{
190
+ type: 'DELETION',
191
+ confidence: 'HIGH',
192
+ ours: 'const config_timeout = 3000;',
193
+ theirs: '',
194
+ start_line: 3,
195
+ end_line: 7,
196
+ }];
197
+ const result2 = selectResolutionStrategy(hunks3, null, learnings);
198
+ // DELETION default => ours, MEDIUM confidence
199
+ // Learnings domain 'config' matches 'config_timeout' in ours text
200
+ // MEDIUM boosted to HIGH
201
+ assert.equal(result2[0].strategy, 'ours');
202
+ assert.equal(result2[0].confidence, 'HIGH');
203
+ assert.ok(result2[0].reasoning.includes('boosted'));
204
+ });
205
+
206
+ it('handles multiple hunks returning strategy per hunk', () => {
207
+ const hunks = [
208
+ { type: 'ADDITIVE', confidence: 'HIGH', ours: '', theirs: 'new line', start_line: 2, end_line: 6 },
209
+ { type: 'DELETION', confidence: 'HIGH', ours: 'old line', theirs: '', start_line: 8, end_line: 12 },
210
+ { type: 'DIVERGENT', confidence: 'LOW', ours: 'const a = 1;', theirs: 'const a = 2;', start_line: 14, end_line: 18 },
211
+ ];
212
+ const result = selectResolutionStrategy(hunks, null, null);
213
+ assert.equal(result.length, 3);
214
+ assert.equal(result[0].hunk_index, 0);
215
+ assert.equal(result[1].hunk_index, 1);
216
+ assert.equal(result[2].hunk_index, 2);
217
+ assert.equal(result[0].strategy, 'theirs'); // ADDITIVE, ours empty
218
+ assert.equal(result[1].strategy, 'ours'); // DELETION, keep content
219
+ assert.equal(result[2].strategy, 'escalate'); // DIVERGENT, no context
220
+ });
221
+
222
+ });
223
+
224
+ // ─── resolveFileContent ──────────────────────────────────────────────────────
225
+
226
+ describe('resolveFileContent', () => {
227
+
228
+ it('resolves ours strategy by keeping ours content', () => {
229
+ const content = buildConflictFile([{ ours: 'const x = 1;', theirs: 'const x = 2;' }]);
230
+ const strategies = [{ hunk_index: 0, strategy: 'ours' }];
231
+ const result = resolveFileContent(content, strategies, null);
232
+ assert.ok(result.resolved_content.includes('const x = 1;'));
233
+ assert.ok(!result.resolved_content.includes('const x = 2;'));
234
+ assert.ok(!result.resolved_content.includes('<<<<<<<'));
235
+ assert.equal(result.applied, 1);
236
+ assert.equal(result.escalated, 0);
237
+ assert.equal(result.has_unresolved, false);
238
+ });
239
+
240
+ it('resolves theirs strategy by keeping theirs content', () => {
241
+ const content = buildConflictFile([{ ours: 'const x = 1;', theirs: 'const x = 2;' }]);
242
+ const strategies = [{ hunk_index: 0, strategy: 'theirs' }];
243
+ const result = resolveFileContent(content, strategies, null);
244
+ assert.ok(result.resolved_content.includes('const x = 2;'));
245
+ assert.ok(!result.resolved_content.includes('const x = 1;'));
246
+ assert.ok(!result.resolved_content.includes('<<<<<<<'));
247
+ assert.equal(result.applied, 1);
248
+ assert.equal(result.escalated, 0);
249
+ });
250
+
251
+ it('resolves combined strategy by concatenating both sides', () => {
252
+ const content = buildConflictFile([{ ours: 'const a = 1;', theirs: 'const b = 2;' }]);
253
+ const strategies = [{ hunk_index: 0, strategy: 'combined' }];
254
+ const result = resolveFileContent(content, strategies, null);
255
+ assert.ok(result.resolved_content.includes('const a = 1;'));
256
+ assert.ok(result.resolved_content.includes('const b = 2;'));
257
+ assert.ok(!result.resolved_content.includes('<<<<<<<'));
258
+ assert.equal(result.applied, 1);
259
+ });
260
+
261
+ it('leaves escalated hunks with conflict markers', () => {
262
+ const content = buildConflictFile([{ ours: 'const x = 1;', theirs: 'const x = 2;' }]);
263
+ const strategies = [{ hunk_index: 0, strategy: 'escalate' }];
264
+ const result = resolveFileContent(content, strategies, null);
265
+ assert.ok(result.resolved_content.includes('<<<<<<< ours'));
266
+ assert.ok(result.resolved_content.includes('======='));
267
+ assert.ok(result.resolved_content.includes('>>>>>>> theirs'));
268
+ assert.equal(result.has_unresolved, true);
269
+ assert.equal(result.escalated, 1);
270
+ assert.equal(result.applied, 0);
271
+ });
272
+
273
+ it('handles multi-hunk file with mixed strategies', () => {
274
+ const content = buildConflictFile([
275
+ { ours: 'const a = 1;', theirs: 'const a = 2;' },
276
+ { ours: 'const b = 10;', theirs: 'const b = 20;' },
277
+ { ours: 'const c = 100;', theirs: 'const c = 200;' },
278
+ ]);
279
+ const strategies = [
280
+ { hunk_index: 0, strategy: 'ours' },
281
+ { hunk_index: 1, strategy: 'theirs' },
282
+ { hunk_index: 2, strategy: 'combined' },
283
+ ];
284
+ const result = resolveFileContent(content, strategies, null);
285
+ assert.ok(result.resolved_content.includes('const a = 1;')); // ours
286
+ assert.ok(!result.resolved_content.includes('const a = 2;'));
287
+ assert.ok(result.resolved_content.includes('const b = 20;')); // theirs
288
+ assert.ok(!result.resolved_content.includes('const b = 10;'));
289
+ assert.ok(result.resolved_content.includes('const c = 100;')); // combined
290
+ assert.ok(result.resolved_content.includes('const c = 200;'));
291
+ assert.equal(result.applied, 3);
292
+ assert.equal(result.escalated, 0);
293
+ assert.equal(result.has_unresolved, false);
294
+ });
295
+
296
+ it('deduplicates require lines in combined structural resolution', () => {
297
+ const oursImports = "const fs = require('fs');\nconst path = require('path');";
298
+ const theirsImports = "const path = require('path');\nconst os = require('os');";
299
+ const content = buildConflictFile([{ ours: oursImports, theirs: theirsImports }]);
300
+ const strategies = [{ hunk_index: 0, strategy: 'combined' }];
301
+ const result = resolveFileContent(content, strategies, null);
302
+ // Should deduplicate: path appears once, sorted alphabetically
303
+ const lines = result.resolved_content.split('\n');
304
+ const requireLines = lines.filter(l => l.includes('require('));
305
+ assert.equal(requireLines.length, 3, 'Should have 3 unique require lines (fs, os, path)');
306
+ // Check alphabetical order
307
+ const requireTexts = requireLines.map(l => l.trim());
308
+ const sorted = [...requireTexts].sort((a, b) => a.localeCompare(b));
309
+ assert.deepEqual(requireTexts, sorted, 'Require lines should be alphabetically sorted');
310
+ assert.equal(result.applied, 1);
311
+ });
312
+
313
+ it('returns correct applied and escalated counts', () => {
314
+ const content = buildConflictFile([
315
+ { ours: 'line1', theirs: 'line2' },
316
+ { ours: 'line3', theirs: 'line4' },
317
+ { ours: 'line5', theirs: 'line6' },
318
+ ]);
319
+ const strategies = [
320
+ { hunk_index: 0, strategy: 'ours' },
321
+ { hunk_index: 1, strategy: 'theirs' },
322
+ { hunk_index: 2, strategy: 'escalate' },
323
+ ];
324
+ const result = resolveFileContent(content, strategies, null);
325
+ assert.equal(result.applied, 2);
326
+ assert.equal(result.escalated, 1);
327
+ assert.equal(result.has_unresolved, true);
328
+ });
329
+
330
+ it('handles file with no conflict markers gracefully', () => {
331
+ const content = '// normal file\nconst x = 1;\nmodule.exports = { x };\n';
332
+ const strategies = [];
333
+ const result = resolveFileContent(content, strategies, null);
334
+ assert.equal(result.resolved_content, content);
335
+ assert.equal(result.applied, 0);
336
+ assert.equal(result.escalated, 0);
337
+ assert.equal(result.has_unresolved, false);
338
+ });
339
+
340
+ });
341
+
342
+ // ─── detectSemanticConflicts ─────────────────────────────────────────────────
343
+
344
+ describe('detectSemanticConflicts', () => {
345
+
346
+ it('returns empty array when no files have dual-branch modifications', () => {
347
+ // Test with a non-existent cwd so git commands return nothing useful
348
+ // detectSemanticConflicts checks git log for MERGE_HEAD..HEAD and HEAD..MERGE_HEAD
349
+ // In a non-merge state, these return empty or error, so no warnings
350
+ const result = detectSemanticConflicts('/tmp/nonexistent-dir-' + Date.now(), [], null);
351
+ assert.deepEqual(result, []);
352
+ });
353
+
354
+ it('returns empty array for empty file list', () => {
355
+ const result = detectSemanticConflicts(process.cwd(), [], null);
356
+ assert.deepEqual(result, []);
357
+ });
358
+
359
+ it('includes domain information when plan context available', () => {
360
+ // This test verifies the function signature and output shape
361
+ // Without a real merge state, the function won't find dual-branch modifications
362
+ const planContext = [
363
+ { task_name: 'Update parser', action_excerpt: 'modify parser logic' },
364
+ ];
365
+ const result = detectSemanticConflicts('/tmp/nonexistent-dir-' + Date.now(), ['parser.js'], planContext);
366
+ // No real git repo so no warnings generated, but function handles gracefully
367
+ assert.ok(Array.isArray(result));
368
+ });
369
+
370
+ it('returns risk level based on domain overlap', () => {
371
+ // Verify the output shape when warnings are present
372
+ // We can test this by creating a mock result structure
373
+ // The function itself requires a real merge state to produce warnings
374
+ // Testing the expected shape: { file, domain, ours_changes, theirs_changes, risk }
375
+ const result = detectSemanticConflicts('/tmp/nonexistent-dir-' + Date.now(), [], null);
376
+ assert.ok(Array.isArray(result));
377
+ // Verify it handles gracefully even without real git state
378
+ assert.equal(result.length, 0);
379
+ });
380
+
381
+ });
382
+
383
+ // ─── createLearningContext and mergeLearningContexts ─────────────────────────
384
+
385
+ describe('createLearningContext and mergeLearningContexts', () => {
386
+
387
+ it('creates learning context from resolution results', () => {
388
+ const resolutionResults = [
389
+ {
390
+ file: 'src/config.cjs',
391
+ strategies: [{ strategy: 'ours', confidence: 'HIGH' }],
392
+ result: { applied: 1, escalated: 0 },
393
+ },
394
+ {
395
+ file: 'src/utils.cjs',
396
+ strategies: [{ strategy: 'combined', confidence: 'MEDIUM' }, { strategy: 'theirs', confidence: 'HIGH' }],
397
+ result: { applied: 2, escalated: 0 },
398
+ },
399
+ ];
400
+ const ctx = createLearningContext(resolutionResults);
401
+ assert.deepEqual(ctx.files_resolved, ['src/config.cjs', 'src/utils.cjs']);
402
+ assert.equal(ctx.strategies_used.length, 3);
403
+ assert.ok(ctx.domain_changes.config);
404
+ assert.ok(ctx.domain_changes.utils);
405
+ assert.equal(ctx.phase_merges_completed, 1);
406
+ });
407
+
408
+ it('merges two learning contexts preserving all entries', () => {
409
+ const ctx1 = {
410
+ files_resolved: ['a.cjs'],
411
+ strategies_used: [{ file: 'a.cjs', strategy: 'ours' }],
412
+ domain_changes: { a: 'Resolved via ours' },
413
+ phase_merges_completed: 1,
414
+ };
415
+ const ctx2 = {
416
+ files_resolved: ['b.cjs'],
417
+ strategies_used: [{ file: 'b.cjs', strategy: 'theirs' }],
418
+ domain_changes: { b: 'Resolved via theirs' },
419
+ phase_merges_completed: 1,
420
+ };
421
+ const merged = mergeLearningContexts(ctx1, ctx2);
422
+ assert.deepEqual(merged.files_resolved, ['a.cjs', 'b.cjs']);
423
+ assert.equal(merged.strategies_used.length, 2);
424
+ assert.ok(merged.domain_changes.a);
425
+ assert.ok(merged.domain_changes.b);
426
+ assert.equal(merged.phase_merges_completed, 2);
427
+ });
428
+
429
+ it('handles empty existing context on first merge', () => {
430
+ const newCtx = {
431
+ files_resolved: ['first.cjs'],
432
+ strategies_used: [{ file: 'first.cjs', strategy: 'combined' }],
433
+ domain_changes: { first: 'Resolved via combined' },
434
+ phase_merges_completed: 1,
435
+ };
436
+ const merged = mergeLearningContexts(null, newCtx);
437
+ assert.deepEqual(merged.files_resolved, ['first.cjs']);
438
+ assert.equal(merged.strategies_used.length, 1);
439
+ assert.equal(merged.phase_merges_completed, 1);
440
+ });
441
+
442
+ it('accumulates across 3 sequential merges', () => {
443
+ const ctx1 = createLearningContext([{ file: 'a.cjs', strategies: [{ strategy: 'ours' }], result: {} }]);
444
+ const ctx2 = createLearningContext([{ file: 'b.cjs', strategies: [{ strategy: 'theirs' }], result: {} }]);
445
+ const ctx3 = createLearningContext([{ file: 'c.cjs', strategies: [{ strategy: 'combined' }], result: {} }]);
446
+
447
+ let acc = mergeLearningContexts(null, ctx1);
448
+ acc = mergeLearningContexts(acc, ctx2);
449
+ acc = mergeLearningContexts(acc, ctx3);
450
+
451
+ assert.deepEqual(acc.files_resolved, ['a.cjs', 'b.cjs', 'c.cjs']);
452
+ assert.equal(acc.strategies_used.length, 3);
453
+ assert.equal(acc.phase_merges_completed, 3);
454
+ assert.ok(acc.domain_changes.a);
455
+ assert.ok(acc.domain_changes.b);
456
+ assert.ok(acc.domain_changes.c);
457
+ });
458
+
459
+ });
460
+
461
+ // ─── buildResolutionReport ───────────────────────────────────────────────────
462
+
463
+ describe('buildResolutionReport', () => {
464
+
465
+ const sampleFileResults = [
466
+ {
467
+ file: 'src/config.cjs',
468
+ hunks: [{
469
+ type: 'ADDITIVE',
470
+ start_line: 2,
471
+ end_line: 6,
472
+ ours: '',
473
+ theirs: 'const newConfig = true;',
474
+ }],
475
+ strategies: [{
476
+ hunk_index: 0,
477
+ strategy: 'theirs',
478
+ confidence: 'HIGH',
479
+ reasoning: 'Additive hunk: theirs adds new content',
480
+ }],
481
+ result: { applied: 1, escalated: 0 },
482
+ },
483
+ {
484
+ file: 'src/utils.cjs',
485
+ hunks: [{
486
+ type: 'DIVERGENT',
487
+ start_line: 10,
488
+ end_line: 14,
489
+ ours: 'const val = 1;',
490
+ theirs: 'const val = 2;',
491
+ }],
492
+ strategies: [{
493
+ hunk_index: 0,
494
+ strategy: 'escalate',
495
+ confidence: 'LOW',
496
+ reasoning: 'Divergent hunk: no plan context',
497
+ }],
498
+ result: { applied: 0, escalated: 1 },
499
+ },
500
+ ];
501
+
502
+ const sampleMetadata = {
503
+ branches: { ours: 'main', theirs: 'feature-x' },
504
+ phase: '01-test',
505
+ timestamp: '2026-02-27T00:00:00Z',
506
+ semantic_warnings: [
507
+ { file: 'src/shared.cjs', domain: 'shared', ours_changes: 'add helper', theirs_changes: 'modify helper', risk: 'warning' },
508
+ ],
509
+ };
510
+
511
+ it('produces valid markdown with all sections', () => {
512
+ const report = buildResolutionReport(sampleFileResults, sampleMetadata);
513
+ assert.ok(report.includes('# Conflict Resolution Report'));
514
+ assert.ok(report.includes('## Resolved Conflicts'));
515
+ assert.ok(report.includes('## Semantic Warnings'));
516
+ assert.ok(report.includes('## Summary'));
517
+ });
518
+
519
+ it('includes per-file detail with truncated before/after snippets', () => {
520
+ const report = buildResolutionReport(sampleFileResults, sampleMetadata);
521
+ assert.ok(report.includes('src/config.cjs'));
522
+ assert.ok(report.includes('src/utils.cjs'));
523
+ // Should include ours/theirs side snippets
524
+ assert.ok(report.includes('Ours side (truncated)') || report.includes('Theirs side (truncated)'));
525
+ });
526
+
527
+ it('includes confidence breakdown in summary', () => {
528
+ const report = buildResolutionReport(sampleFileResults, sampleMetadata);
529
+ // Should have confidence count table
530
+ assert.ok(report.includes('| HIGH |'));
531
+ assert.ok(report.includes('| MEDIUM |'));
532
+ assert.ok(report.includes('| LOW |'));
533
+ });
534
+
535
+ it('includes semantic warnings section', () => {
536
+ const report = buildResolutionReport(sampleFileResults, sampleMetadata);
537
+ assert.ok(report.includes('src/shared.cjs'));
538
+ assert.ok(report.includes('warning'));
539
+ assert.ok(report.includes('shared'));
540
+ });
541
+
542
+ it('shows no semantic warnings message when array is empty', () => {
543
+ const metaNoWarnings = { ...sampleMetadata, semantic_warnings: [] };
544
+ const report = buildResolutionReport(sampleFileResults, metaNoWarnings);
545
+ assert.ok(report.includes('No semantic conflicts detected'));
546
+ });
547
+
548
+ it('includes overall assessment based on confidence levels', () => {
549
+ const report = buildResolutionReport(sampleFileResults, sampleMetadata);
550
+ // Has LOW confidence, so assessment should recommend review
551
+ assert.ok(report.includes('Manual review recommended') || report.includes('LOW confidence'));
552
+ });
553
+
554
+ });