@lumenflow/core 1.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 (263) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +119 -0
  3. package/dist/active-wu-detector.d.ts +33 -0
  4. package/dist/active-wu-detector.js +106 -0
  5. package/dist/adapters/filesystem-metrics.adapter.d.ts +108 -0
  6. package/dist/adapters/filesystem-metrics.adapter.js +519 -0
  7. package/dist/adapters/terminal-renderer.adapter.d.ts +106 -0
  8. package/dist/adapters/terminal-renderer.adapter.js +337 -0
  9. package/dist/arg-parser.d.ts +63 -0
  10. package/dist/arg-parser.js +560 -0
  11. package/dist/backlog-editor.d.ts +98 -0
  12. package/dist/backlog-editor.js +179 -0
  13. package/dist/backlog-generator.d.ts +111 -0
  14. package/dist/backlog-generator.js +381 -0
  15. package/dist/backlog-parser.d.ts +45 -0
  16. package/dist/backlog-parser.js +102 -0
  17. package/dist/backlog-sync-validator.d.ts +78 -0
  18. package/dist/backlog-sync-validator.js +294 -0
  19. package/dist/branch-drift.d.ts +34 -0
  20. package/dist/branch-drift.js +51 -0
  21. package/dist/cleanup-install-config.d.ts +33 -0
  22. package/dist/cleanup-install-config.js +37 -0
  23. package/dist/cleanup-lock.d.ts +139 -0
  24. package/dist/cleanup-lock.js +313 -0
  25. package/dist/code-path-validator.d.ts +146 -0
  26. package/dist/code-path-validator.js +537 -0
  27. package/dist/code-paths-overlap.d.ts +55 -0
  28. package/dist/code-paths-overlap.js +245 -0
  29. package/dist/commands-logger.d.ts +77 -0
  30. package/dist/commands-logger.js +254 -0
  31. package/dist/commit-message-utils.d.ts +25 -0
  32. package/dist/commit-message-utils.js +41 -0
  33. package/dist/compliance-parser.d.ts +150 -0
  34. package/dist/compliance-parser.js +507 -0
  35. package/dist/constants/backlog-patterns.d.ts +20 -0
  36. package/dist/constants/backlog-patterns.js +23 -0
  37. package/dist/constants/dora-constants.d.ts +49 -0
  38. package/dist/constants/dora-constants.js +53 -0
  39. package/dist/constants/gate-constants.d.ts +15 -0
  40. package/dist/constants/gate-constants.js +15 -0
  41. package/dist/constants/linter-constants.d.ts +16 -0
  42. package/dist/constants/linter-constants.js +16 -0
  43. package/dist/constants/tokenizer-constants.d.ts +15 -0
  44. package/dist/constants/tokenizer-constants.js +15 -0
  45. package/dist/core/scope-checker.d.ts +97 -0
  46. package/dist/core/scope-checker.js +163 -0
  47. package/dist/core/tool-runner.d.ts +161 -0
  48. package/dist/core/tool-runner.js +393 -0
  49. package/dist/core/tool.constants.d.ts +105 -0
  50. package/dist/core/tool.constants.js +101 -0
  51. package/dist/core/tool.schemas.d.ts +226 -0
  52. package/dist/core/tool.schemas.js +226 -0
  53. package/dist/core/worktree-guard.d.ts +130 -0
  54. package/dist/core/worktree-guard.js +242 -0
  55. package/dist/coverage-gate.d.ts +108 -0
  56. package/dist/coverage-gate.js +196 -0
  57. package/dist/date-utils.d.ts +75 -0
  58. package/dist/date-utils.js +140 -0
  59. package/dist/dependency-graph.d.ts +142 -0
  60. package/dist/dependency-graph.js +550 -0
  61. package/dist/dependency-guard.d.ts +54 -0
  62. package/dist/dependency-guard.js +142 -0
  63. package/dist/dependency-validator.d.ts +105 -0
  64. package/dist/dependency-validator.js +154 -0
  65. package/dist/docs-path-validator.d.ts +36 -0
  66. package/dist/docs-path-validator.js +95 -0
  67. package/dist/domain/orchestration.constants.d.ts +99 -0
  68. package/dist/domain/orchestration.constants.js +97 -0
  69. package/dist/domain/orchestration.schemas.d.ts +280 -0
  70. package/dist/domain/orchestration.schemas.js +211 -0
  71. package/dist/domain/orchestration.types.d.ts +133 -0
  72. package/dist/domain/orchestration.types.js +12 -0
  73. package/dist/error-handler.d.ts +116 -0
  74. package/dist/error-handler.js +136 -0
  75. package/dist/file-classifiers.d.ts +62 -0
  76. package/dist/file-classifiers.js +108 -0
  77. package/dist/gates-agent-mode.d.ts +81 -0
  78. package/dist/gates-agent-mode.js +94 -0
  79. package/dist/generate-traceability.d.ts +107 -0
  80. package/dist/generate-traceability.js +411 -0
  81. package/dist/git-adapter.d.ts +395 -0
  82. package/dist/git-adapter.js +649 -0
  83. package/dist/git-staged-validator.d.ts +32 -0
  84. package/dist/git-staged-validator.js +48 -0
  85. package/dist/hardcoded-strings.d.ts +61 -0
  86. package/dist/hardcoded-strings.js +270 -0
  87. package/dist/incremental-lint.d.ts +78 -0
  88. package/dist/incremental-lint.js +129 -0
  89. package/dist/incremental-test.d.ts +39 -0
  90. package/dist/incremental-test.js +61 -0
  91. package/dist/index.d.ts +42 -0
  92. package/dist/index.js +61 -0
  93. package/dist/invariants/check-automated-tests.d.ts +50 -0
  94. package/dist/invariants/check-automated-tests.js +166 -0
  95. package/dist/invariants-runner.d.ts +103 -0
  96. package/dist/invariants-runner.js +527 -0
  97. package/dist/lane-checker.d.ts +50 -0
  98. package/dist/lane-checker.js +319 -0
  99. package/dist/lane-inference.d.ts +39 -0
  100. package/dist/lane-inference.js +195 -0
  101. package/dist/lane-lock.d.ts +211 -0
  102. package/dist/lane-lock.js +474 -0
  103. package/dist/lane-validator.d.ts +48 -0
  104. package/dist/lane-validator.js +114 -0
  105. package/dist/logs-lib.d.ts +104 -0
  106. package/dist/logs-lib.js +207 -0
  107. package/dist/lumenflow-config-schema.d.ts +272 -0
  108. package/dist/lumenflow-config-schema.js +207 -0
  109. package/dist/lumenflow-config.d.ts +95 -0
  110. package/dist/lumenflow-config.js +236 -0
  111. package/dist/manual-test-validator.d.ts +80 -0
  112. package/dist/manual-test-validator.js +200 -0
  113. package/dist/merge-lock.d.ts +115 -0
  114. package/dist/merge-lock.js +251 -0
  115. package/dist/micro-worktree.d.ts +159 -0
  116. package/dist/micro-worktree.js +427 -0
  117. package/dist/migration-deployer.d.ts +69 -0
  118. package/dist/migration-deployer.js +151 -0
  119. package/dist/orchestration-advisory-loader.d.ts +28 -0
  120. package/dist/orchestration-advisory-loader.js +87 -0
  121. package/dist/orchestration-advisory.d.ts +58 -0
  122. package/dist/orchestration-advisory.js +94 -0
  123. package/dist/orchestration-di.d.ts +48 -0
  124. package/dist/orchestration-di.js +57 -0
  125. package/dist/orchestration-rules.d.ts +57 -0
  126. package/dist/orchestration-rules.js +201 -0
  127. package/dist/orphan-detector.d.ts +131 -0
  128. package/dist/orphan-detector.js +226 -0
  129. package/dist/path-classifiers.d.ts +57 -0
  130. package/dist/path-classifiers.js +93 -0
  131. package/dist/piped-command-detector.d.ts +34 -0
  132. package/dist/piped-command-detector.js +64 -0
  133. package/dist/ports/dashboard-renderer.port.d.ts +112 -0
  134. package/dist/ports/dashboard-renderer.port.js +25 -0
  135. package/dist/ports/metrics-collector.port.d.ts +132 -0
  136. package/dist/ports/metrics-collector.port.js +26 -0
  137. package/dist/process-detector.d.ts +84 -0
  138. package/dist/process-detector.js +172 -0
  139. package/dist/prompt-linter.d.ts +72 -0
  140. package/dist/prompt-linter.js +312 -0
  141. package/dist/prompt-monitor.d.ts +15 -0
  142. package/dist/prompt-monitor.js +205 -0
  143. package/dist/rebase-artifact-cleanup.d.ts +145 -0
  144. package/dist/rebase-artifact-cleanup.js +433 -0
  145. package/dist/retry-strategy.d.ts +189 -0
  146. package/dist/retry-strategy.js +283 -0
  147. package/dist/risk-detector.d.ts +108 -0
  148. package/dist/risk-detector.js +252 -0
  149. package/dist/rollback-utils.d.ts +76 -0
  150. package/dist/rollback-utils.js +104 -0
  151. package/dist/section-headings.d.ts +43 -0
  152. package/dist/section-headings.js +49 -0
  153. package/dist/spawn-escalation.d.ts +90 -0
  154. package/dist/spawn-escalation.js +253 -0
  155. package/dist/spawn-monitor.d.ts +229 -0
  156. package/dist/spawn-monitor.js +672 -0
  157. package/dist/spawn-recovery.d.ts +82 -0
  158. package/dist/spawn-recovery.js +298 -0
  159. package/dist/spawn-registry-schema.d.ts +98 -0
  160. package/dist/spawn-registry-schema.js +108 -0
  161. package/dist/spawn-registry-store.d.ts +146 -0
  162. package/dist/spawn-registry-store.js +273 -0
  163. package/dist/spawn-tree.d.ts +121 -0
  164. package/dist/spawn-tree.js +285 -0
  165. package/dist/stamp-status-validator.d.ts +84 -0
  166. package/dist/stamp-status-validator.js +134 -0
  167. package/dist/stamp-utils.d.ts +100 -0
  168. package/dist/stamp-utils.js +229 -0
  169. package/dist/state-machine.d.ts +26 -0
  170. package/dist/state-machine.js +83 -0
  171. package/dist/system-map-validator.d.ts +80 -0
  172. package/dist/system-map-validator.js +272 -0
  173. package/dist/telemetry.d.ts +80 -0
  174. package/dist/telemetry.js +213 -0
  175. package/dist/token-counter.d.ts +51 -0
  176. package/dist/token-counter.js +145 -0
  177. package/dist/usecases/get-dashboard-data.usecase.d.ts +52 -0
  178. package/dist/usecases/get-dashboard-data.usecase.js +61 -0
  179. package/dist/usecases/get-suggestions.usecase.d.ts +100 -0
  180. package/dist/usecases/get-suggestions.usecase.js +153 -0
  181. package/dist/user-normalizer.d.ts +41 -0
  182. package/dist/user-normalizer.js +141 -0
  183. package/dist/validators/phi-constants.d.ts +97 -0
  184. package/dist/validators/phi-constants.js +152 -0
  185. package/dist/validators/phi-scanner.d.ts +58 -0
  186. package/dist/validators/phi-scanner.js +215 -0
  187. package/dist/worktree-ownership.d.ts +50 -0
  188. package/dist/worktree-ownership.js +74 -0
  189. package/dist/worktree-scanner.d.ts +103 -0
  190. package/dist/worktree-scanner.js +168 -0
  191. package/dist/worktree-symlink.d.ts +99 -0
  192. package/dist/worktree-symlink.js +359 -0
  193. package/dist/wu-backlog-updater.d.ts +17 -0
  194. package/dist/wu-backlog-updater.js +37 -0
  195. package/dist/wu-checkpoint.d.ts +124 -0
  196. package/dist/wu-checkpoint.js +233 -0
  197. package/dist/wu-claim-helpers.d.ts +26 -0
  198. package/dist/wu-claim-helpers.js +63 -0
  199. package/dist/wu-claim-resume.d.ts +106 -0
  200. package/dist/wu-claim-resume.js +276 -0
  201. package/dist/wu-consistency-checker.d.ts +95 -0
  202. package/dist/wu-consistency-checker.js +567 -0
  203. package/dist/wu-constants.d.ts +1275 -0
  204. package/dist/wu-constants.js +1382 -0
  205. package/dist/wu-create-validators.d.ts +42 -0
  206. package/dist/wu-create-validators.js +93 -0
  207. package/dist/wu-done-branch-only.d.ts +63 -0
  208. package/dist/wu-done-branch-only.js +191 -0
  209. package/dist/wu-done-messages.d.ts +119 -0
  210. package/dist/wu-done-messages.js +185 -0
  211. package/dist/wu-done-pr.d.ts +72 -0
  212. package/dist/wu-done-pr.js +174 -0
  213. package/dist/wu-done-retry-helpers.d.ts +85 -0
  214. package/dist/wu-done-retry-helpers.js +172 -0
  215. package/dist/wu-done-ui.d.ts +37 -0
  216. package/dist/wu-done-ui.js +69 -0
  217. package/dist/wu-done-validators.d.ts +411 -0
  218. package/dist/wu-done-validators.js +1229 -0
  219. package/dist/wu-done-worktree.d.ts +182 -0
  220. package/dist/wu-done-worktree.js +1097 -0
  221. package/dist/wu-helpers.d.ts +128 -0
  222. package/dist/wu-helpers.js +248 -0
  223. package/dist/wu-lint.d.ts +70 -0
  224. package/dist/wu-lint.js +234 -0
  225. package/dist/wu-paths.d.ts +171 -0
  226. package/dist/wu-paths.js +178 -0
  227. package/dist/wu-preflight-validators.d.ts +86 -0
  228. package/dist/wu-preflight-validators.js +251 -0
  229. package/dist/wu-recovery.d.ts +138 -0
  230. package/dist/wu-recovery.js +341 -0
  231. package/dist/wu-repair-core.d.ts +131 -0
  232. package/dist/wu-repair-core.js +669 -0
  233. package/dist/wu-schema-normalization.d.ts +17 -0
  234. package/dist/wu-schema-normalization.js +82 -0
  235. package/dist/wu-schema.d.ts +793 -0
  236. package/dist/wu-schema.js +881 -0
  237. package/dist/wu-spawn-helpers.d.ts +121 -0
  238. package/dist/wu-spawn-helpers.js +271 -0
  239. package/dist/wu-spawn.d.ts +158 -0
  240. package/dist/wu-spawn.js +1306 -0
  241. package/dist/wu-state-schema.d.ts +213 -0
  242. package/dist/wu-state-schema.js +156 -0
  243. package/dist/wu-state-store.d.ts +264 -0
  244. package/dist/wu-state-store.js +691 -0
  245. package/dist/wu-status-transition.d.ts +63 -0
  246. package/dist/wu-status-transition.js +382 -0
  247. package/dist/wu-status-updater.d.ts +25 -0
  248. package/dist/wu-status-updater.js +116 -0
  249. package/dist/wu-transaction-collectors.d.ts +116 -0
  250. package/dist/wu-transaction-collectors.js +272 -0
  251. package/dist/wu-transaction.d.ts +170 -0
  252. package/dist/wu-transaction.js +273 -0
  253. package/dist/wu-validation-constants.d.ts +60 -0
  254. package/dist/wu-validation-constants.js +66 -0
  255. package/dist/wu-validation.d.ts +118 -0
  256. package/dist/wu-validation.js +243 -0
  257. package/dist/wu-validator.d.ts +62 -0
  258. package/dist/wu-validator.js +325 -0
  259. package/dist/wu-yaml-fixer.d.ts +97 -0
  260. package/dist/wu-yaml-fixer.js +264 -0
  261. package/dist/wu-yaml.d.ts +86 -0
  262. package/dist/wu-yaml.js +222 -0
  263. package/package.json +114 -0
@@ -0,0 +1,138 @@
1
+ /**
2
+ * WU Recovery Module
3
+ *
4
+ * Handles zombie state recovery (status=done but worktree still exists)
5
+ * Fixes WU-1159 gap by ensuring status.md AND backlog.md are updated
6
+ *
7
+ * Zombie states occur when wu:done crashes mid-execution:
8
+ * - WU YAML updated to status=done
9
+ * - But cleanup incomplete (worktree exists, docs not updated, etc.)
10
+ *
11
+ * Recovery mode is idempotent - safe to run multiple times
12
+ *
13
+ * NOTE (WU-1826): Core recovery functions are now re-exported from
14
+ * tools/lib/wu-repair-core.mjs for use by the unified wu:repair command.
15
+ * This module remains the canonical implementation used by wu-done.mjs.
16
+ */
17
+ /**
18
+ * WU-1335: Maximum number of recovery attempts before escalating to manual intervention
19
+ * WU-1747: Now derived from the recovery retry preset for consistency
20
+ */
21
+ export declare const MAX_RECOVERY_ATTEMPTS: 4;
22
+ /**
23
+ * WU-1335: Get the path to the recovery marker file for a WU
24
+ *
25
+ * @param {string} id - WU ID
26
+ * @param {string} [baseDir=process.cwd()] - Base directory for .beacon
27
+ * @returns {string} Path to recovery marker file
28
+ */
29
+ export declare function getRecoveryMarkerPath(id: any, baseDir?: string): string;
30
+ /**
31
+ * WU-1335: Get the current recovery attempt count for a WU
32
+ *
33
+ * @param {string} id - WU ID
34
+ * @param {string} [baseDir=process.cwd()] - Base directory for .beacon
35
+ * @returns {number} Current attempt count (0 if no marker exists)
36
+ */
37
+ export declare function getRecoveryAttemptCount(id: any, baseDir?: string): any;
38
+ /**
39
+ * WU-1335: Increment recovery attempt count for a WU
40
+ *
41
+ * @param {string} id - WU ID
42
+ * @param {string} [baseDir=process.cwd()] - Base directory for .beacon
43
+ * @returns {number} New attempt count
44
+ */
45
+ export declare function incrementRecoveryAttempt(id: any, baseDir?: string): any;
46
+ /**
47
+ * WU-1335: Clear recovery attempts for a WU (called on successful recovery)
48
+ *
49
+ * @param {string} id - WU ID
50
+ * @param {string} [baseDir=process.cwd()] - Base directory for .beacon
51
+ */
52
+ export declare function clearRecoveryAttempts(id: any, baseDir?: string): void;
53
+ /**
54
+ * WU-1335: Check if recovery should escalate to manual intervention
55
+ *
56
+ * @param {number} attempts - Current attempt count
57
+ * @returns {boolean} True if should escalate
58
+ */
59
+ export declare function shouldEscalateToManualIntervention(attempts: any): boolean;
60
+ /**
61
+ * Detect zombie state
62
+ *
63
+ * @param {object} doc - WU YAML document
64
+ * @param {string|null} worktreePath - Path to worktree
65
+ * @returns {boolean} True if zombie state detected
66
+ */
67
+ export declare function detectZombieState(doc: any, worktreePath: any): boolean;
68
+ /**
69
+ * WU-1440: Reset worktree WU YAML to in_progress for recovery
70
+ *
71
+ * Instead of committing directly to main (old recovery mode), this function
72
+ * resets the worktree YAML so the normal wu:done merge flow can proceed.
73
+ *
74
+ * Removes completion markers:
75
+ * - status → in_progress
76
+ * - locked → removed
77
+ * - completed_at → removed
78
+ *
79
+ * Preserves all other fields (description, acceptance, code_paths, etc.)
80
+ *
81
+ * @param {object} params - Recovery parameters
82
+ * @param {string} params.worktreePath - Path to worktree root
83
+ * @param {string} params.id - WU ID
84
+ * @param {object} params.doc - WU YAML document (will be mutated)
85
+ * @returns {{ reset: boolean }} Reset result
86
+ */
87
+ export declare function resetWorktreeYAMLForRecovery({ worktreePath, id, doc }: {
88
+ worktreePath: any;
89
+ id: any;
90
+ doc: any;
91
+ }): {
92
+ reset: boolean;
93
+ };
94
+ /**
95
+ * Recover from zombie state
96
+ *
97
+ * THE COMPLETE FIX for WU-1159 gap:
98
+ * - Ensures stamp exists
99
+ * - Ensures YAML completion markers
100
+ * - Ensures status.md updated (THE FIX)
101
+ * - Ensures backlog.md updated (THE FIX)
102
+ *
103
+ * All operations are idempotent - safe to run multiple times
104
+ *
105
+ * WU-1303: Added atomic rollback - if any operation fails, all files are
106
+ * restored to their original state to prevent leaving main checkout dirty.
107
+ *
108
+ * @param {object} params - Recovery parameters
109
+ * @param {string} params.id - WU ID
110
+ * @param {object} params.doc - WU YAML document
111
+ * @param {string} params.worktreePath - Path to worktree
112
+ * @param {object} params.args - Command-line args
113
+ * @returns {object} Recovery results
114
+ */
115
+ export declare function recoverZombieState({ id, doc, _worktreePath, _args }: {
116
+ id: any;
117
+ doc: any;
118
+ _worktreePath: any;
119
+ _args: any;
120
+ }): Promise<{
121
+ stamp: {
122
+ created: boolean;
123
+ path: string;
124
+ reason?: string;
125
+ } | null;
126
+ yaml: {
127
+ updated: boolean;
128
+ reason?: string;
129
+ } | null;
130
+ docs: {
131
+ status: unknown | null;
132
+ backlog: unknown | null;
133
+ };
134
+ commit?: {
135
+ committed: boolean;
136
+ reason?: string;
137
+ };
138
+ }>;
@@ -0,0 +1,341 @@
1
+ /**
2
+ * WU Recovery Module
3
+ *
4
+ * Handles zombie state recovery (status=done but worktree still exists)
5
+ * Fixes WU-1159 gap by ensuring status.md AND backlog.md are updated
6
+ *
7
+ * Zombie states occur when wu:done crashes mid-execution:
8
+ * - WU YAML updated to status=done
9
+ * - But cleanup incomplete (worktree exists, docs not updated, etc.)
10
+ *
11
+ * Recovery mode is idempotent - safe to run multiple times
12
+ *
13
+ * NOTE (WU-1826): Core recovery functions are now re-exported from
14
+ * tools/lib/wu-repair-core.mjs for use by the unified wu:repair command.
15
+ * This module remains the canonical implementation used by wu-done.mjs.
16
+ */
17
+ import { existsSync, readFileSync, unlinkSync, writeFileSync, mkdirSync } from 'node:fs';
18
+ import { join, isAbsolute } from 'node:path';
19
+ import { RECOVERY } from './wu-done-messages.js';
20
+ import { createStamp } from './stamp-utils.js';
21
+ import { WU_PATHS } from './wu-paths.js';
22
+ import { writeWU } from './wu-yaml.js';
23
+ import { updateStatusRemoveInProgress, addToStatusCompleted } from './wu-status-updater.js';
24
+ import { moveWUToDoneBacklog } from './wu-backlog-updater.js';
25
+ import { getGitForCwd } from './git-adapter.js';
26
+ import { createError, ErrorCodes } from './error-handler.js';
27
+ import { rollbackFiles } from './rollback-utils.js';
28
+ import { LOG_PREFIX, EMOJI, WU_STATUS, getProjectRoot } from './wu-constants.js';
29
+ import { RETRY_PRESETS } from './retry-strategy.js';
30
+ /**
31
+ * WU-1335: Maximum number of recovery attempts before escalating to manual intervention
32
+ * WU-1747: Now derived from the recovery retry preset for consistency
33
+ */
34
+ export const MAX_RECOVERY_ATTEMPTS = RETRY_PRESETS.recovery.maxAttempts;
35
+ /**
36
+ * WU-1335: Recovery marker subdirectory within .beacon
37
+ */
38
+ const RECOVERY_MARKER_DIR = 'recovery';
39
+ /**
40
+ * WU-1335: Get the path to the recovery marker file for a WU
41
+ *
42
+ * @param {string} id - WU ID
43
+ * @param {string} [baseDir=process.cwd()] - Base directory for .beacon
44
+ * @returns {string} Path to recovery marker file
45
+ */
46
+ export function getRecoveryMarkerPath(id, baseDir = process.cwd()) {
47
+ return join(baseDir, '.beacon', RECOVERY_MARKER_DIR, `${id}.recovery`);
48
+ }
49
+ /**
50
+ * WU-1335: Get the current recovery attempt count for a WU
51
+ *
52
+ * @param {string} id - WU ID
53
+ * @param {string} [baseDir=process.cwd()] - Base directory for .beacon
54
+ * @returns {number} Current attempt count (0 if no marker exists)
55
+ */
56
+ export function getRecoveryAttemptCount(id, baseDir = process.cwd()) {
57
+ const markerPath = getRecoveryMarkerPath(id, baseDir);
58
+ if (!existsSync(markerPath)) {
59
+ return 0;
60
+ }
61
+ try {
62
+ const data = JSON.parse(readFileSync(markerPath, { encoding: 'utf-8' }));
63
+ return data.attempts || 0;
64
+ }
65
+ catch {
66
+ // Corrupted file - treat as 0
67
+ return 0;
68
+ }
69
+ }
70
+ /**
71
+ * WU-1335: Increment recovery attempt count for a WU
72
+ *
73
+ * @param {string} id - WU ID
74
+ * @param {string} [baseDir=process.cwd()] - Base directory for .beacon
75
+ * @returns {number} New attempt count
76
+ */
77
+ export function incrementRecoveryAttempt(id, baseDir = process.cwd()) {
78
+ const markerPath = getRecoveryMarkerPath(id, baseDir);
79
+ const markerDir = join(baseDir, '.beacon', RECOVERY_MARKER_DIR);
80
+ // Ensure directory exists
81
+ if (!existsSync(markerDir)) {
82
+ mkdirSync(markerDir, { recursive: true });
83
+ }
84
+ const currentCount = getRecoveryAttemptCount(id, baseDir);
85
+ const newCount = currentCount + 1;
86
+ const data = {
87
+ attempts: newCount,
88
+ lastAttempt: new Date().toISOString(),
89
+ wuId: id,
90
+ };
91
+ writeFileSync(markerPath, JSON.stringify(data, null, 2));
92
+ return newCount;
93
+ }
94
+ /**
95
+ * WU-1335: Clear recovery attempts for a WU (called on successful recovery)
96
+ *
97
+ * @param {string} id - WU ID
98
+ * @param {string} [baseDir=process.cwd()] - Base directory for .beacon
99
+ */
100
+ export function clearRecoveryAttempts(id, baseDir = process.cwd()) {
101
+ const markerPath = getRecoveryMarkerPath(id, baseDir);
102
+ if (existsSync(markerPath)) {
103
+ unlinkSync(markerPath);
104
+ }
105
+ }
106
+ /**
107
+ * WU-1335: Check if recovery should escalate to manual intervention
108
+ *
109
+ * @param {number} attempts - Current attempt count
110
+ * @returns {boolean} True if should escalate
111
+ */
112
+ export function shouldEscalateToManualIntervention(attempts) {
113
+ return attempts >= MAX_RECOVERY_ATTEMPTS;
114
+ }
115
+ /**
116
+ * Record initial state for atomic rollback (WU-1303)
117
+ *
118
+ * @param {object} paths - Object containing file paths
119
+ * @param {string} paths.wuPath - Path to WU YAML
120
+ * @param {string} paths.statusPath - Path to status.md
121
+ * @param {string} paths.backlogPath - Path to backlog.md
122
+ * @param {string} paths.stampPath - Path to stamp file
123
+ * @returns {object} Transaction state for rollback
124
+ */
125
+ function recordRecoveryState(paths) {
126
+ const { wuPath, statusPath, backlogPath, stampPath } = paths;
127
+ return {
128
+ wuContent: existsSync(wuPath) ? readFileSync(wuPath, { encoding: 'utf-8' }) : null,
129
+ statusContent: existsSync(statusPath) ? readFileSync(statusPath, { encoding: 'utf-8' }) : null,
130
+ backlogContent: existsSync(backlogPath)
131
+ ? readFileSync(backlogPath, { encoding: 'utf-8' })
132
+ : null,
133
+ stampExisted: existsSync(stampPath),
134
+ timestamp: new Date().toISOString(),
135
+ };
136
+ }
137
+ /**
138
+ * Rollback recovery transaction on failure (WU-1303)
139
+ *
140
+ * @param {object} state - Transaction state from recordRecoveryState
141
+ * @param {object} paths - Object containing file paths
142
+ */
143
+ function rollbackRecoveryTransaction(state, paths) {
144
+ const { wuPath, statusPath, backlogPath, stampPath } = paths;
145
+ console.log(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} Rolling back recovery transaction...`);
146
+ // Build list of files to restore
147
+ const filesToRestore = [];
148
+ if (state.wuContent !== null) {
149
+ filesToRestore.push({ name: 'WU YAML', path: wuPath, content: state.wuContent });
150
+ }
151
+ if (state.statusContent !== null) {
152
+ filesToRestore.push({ name: 'status.md', path: statusPath, content: state.statusContent });
153
+ }
154
+ if (state.backlogContent !== null) {
155
+ filesToRestore.push({ name: 'backlog.md', path: backlogPath, content: state.backlogContent });
156
+ }
157
+ // Restore files
158
+ if (filesToRestore.length > 0) {
159
+ const result = rollbackFiles(filesToRestore);
160
+ for (const name of result.restored) {
161
+ console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Restored ${name}`);
162
+ }
163
+ for (const err of result.errors) {
164
+ console.error(`${LOG_PREFIX.DONE} ${EMOJI.FAILURE} Failed to restore ${err.name}: ${err.error}`);
165
+ }
166
+ }
167
+ // Remove stamp if it was created during recovery
168
+ if (!state.stampExisted && existsSync(stampPath)) {
169
+ try {
170
+ unlinkSync(stampPath);
171
+ console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Removed stamp file`);
172
+ }
173
+ catch (err) {
174
+ console.error(`${LOG_PREFIX.DONE} ${EMOJI.FAILURE} Failed to remove stamp: ${err.message}`);
175
+ }
176
+ }
177
+ console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Rollback complete - main checkout is clean`);
178
+ }
179
+ /**
180
+ * Detect zombie state
181
+ *
182
+ * @param {object} doc - WU YAML document
183
+ * @param {string|null} worktreePath - Path to worktree
184
+ * @returns {boolean} True if zombie state detected
185
+ */
186
+ export function detectZombieState(doc, worktreePath) {
187
+ return doc.status === WU_STATUS.DONE && worktreePath && existsSync(worktreePath);
188
+ }
189
+ /**
190
+ * WU-1440: Reset worktree WU YAML to in_progress for recovery
191
+ *
192
+ * Instead of committing directly to main (old recovery mode), this function
193
+ * resets the worktree YAML so the normal wu:done merge flow can proceed.
194
+ *
195
+ * Removes completion markers:
196
+ * - status → in_progress
197
+ * - locked → removed
198
+ * - completed_at → removed
199
+ *
200
+ * Preserves all other fields (description, acceptance, code_paths, etc.)
201
+ *
202
+ * @param {object} params - Recovery parameters
203
+ * @param {string} params.worktreePath - Path to worktree root
204
+ * @param {string} params.id - WU ID
205
+ * @param {object} params.doc - WU YAML document (will be mutated)
206
+ * @returns {{ reset: boolean }} Reset result
207
+ */
208
+ export function resetWorktreeYAMLForRecovery({ worktreePath, id, doc }) {
209
+ const projectRoot = getProjectRoot(import.meta.url);
210
+ const resolvedWorktreeRoot = isAbsolute(worktreePath)
211
+ ? worktreePath
212
+ : join(projectRoot, worktreePath);
213
+ const wtWUPath = join(resolvedWorktreeRoot, WU_PATHS.WU(id));
214
+ // Reset status to in_progress
215
+ doc.status = WU_STATUS.IN_PROGRESS;
216
+ // Remove completion markers (delete from object)
217
+ delete doc.locked;
218
+ delete doc.completed_at;
219
+ // Write updated YAML to worktree
220
+ writeWU(wtWUPath, doc);
221
+ console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Reset worktree YAML to in_progress for recovery`);
222
+ return { reset: true };
223
+ }
224
+ // Note: updateBacklogMd removed (WU-1163) - now uses shared moveWUToDoneBacklog from wu-backlog-updater.mjs
225
+ /**
226
+ * Recover from zombie state
227
+ *
228
+ * THE COMPLETE FIX for WU-1159 gap:
229
+ * - Ensures stamp exists
230
+ * - Ensures YAML completion markers
231
+ * - Ensures status.md updated (THE FIX)
232
+ * - Ensures backlog.md updated (THE FIX)
233
+ *
234
+ * All operations are idempotent - safe to run multiple times
235
+ *
236
+ * WU-1303: Added atomic rollback - if any operation fails, all files are
237
+ * restored to their original state to prevent leaving main checkout dirty.
238
+ *
239
+ * @param {object} params - Recovery parameters
240
+ * @param {string} params.id - WU ID
241
+ * @param {object} params.doc - WU YAML document
242
+ * @param {string} params.worktreePath - Path to worktree
243
+ * @param {object} params.args - Command-line args
244
+ * @returns {object} Recovery results
245
+ */
246
+ export async function recoverZombieState({ id, doc, _worktreePath, _args }) {
247
+ console.log(`\n${RECOVERY.DETECTED}`);
248
+ console.log(RECOVERY.RESUMING);
249
+ console.log(RECOVERY.EXPLANATION);
250
+ const results = {
251
+ stamp: null,
252
+ yaml: null,
253
+ docs: { status: null, backlog: null },
254
+ };
255
+ // WU-1303: Record file state BEFORE any modifications for atomic rollback
256
+ const wuPath = WU_PATHS.WU(id);
257
+ const statusPath = WU_PATHS.STATUS();
258
+ const backlogPath = WU_PATHS.BACKLOG();
259
+ const stampPath = WU_PATHS.STAMP(id);
260
+ const paths = { wuPath, statusPath, backlogPath, stampPath };
261
+ const initialState = recordRecoveryState(paths);
262
+ try {
263
+ // 1. Ensure stamp exists (idempotent)
264
+ console.log(RECOVERY.CREATING_STAMP);
265
+ results.stamp = createStamp({ id, title: doc.title });
266
+ console.log(RECOVERY.STAMP_CREATED);
267
+ // 2. Ensure YAML completion markers (idempotent)
268
+ console.log(RECOVERY.UPDATING_YAML);
269
+ let yamlUpdated = false;
270
+ if (doc.locked !== true) {
271
+ doc.locked = true;
272
+ yamlUpdated = true;
273
+ }
274
+ if (!doc.completed_at && !doc.completed) {
275
+ doc.completed_at = new Date().toISOString();
276
+ yamlUpdated = true;
277
+ }
278
+ if (yamlUpdated) {
279
+ writeWU(wuPath, doc);
280
+ results.yaml = { updated: true };
281
+ }
282
+ else {
283
+ results.yaml = { updated: false, reason: 'already_complete' };
284
+ }
285
+ console.log(RECOVERY.YAML_UPDATED);
286
+ // 3. **FIX WU-1159 GAP**: Update status.md and backlog.md (idempotent)
287
+ console.log(RECOVERY.UPDATING_DOCS);
288
+ updateStatusRemoveInProgress(statusPath, id);
289
+ addToStatusCompleted(statusPath, id, doc.title);
290
+ results.docs.status = { updated: true };
291
+ moveWUToDoneBacklog(backlogPath, id, doc.title);
292
+ results.docs.backlog = { updated: true };
293
+ console.log(RECOVERY.DOCS_UPDATED);
294
+ // 4. **FIX WU-1201**: Commit recovery changes to prevent data loss
295
+ // Check if git status is dirty (files were updated)
296
+ const git = getGitForCwd();
297
+ const statusOutput = await git.getStatus();
298
+ if (statusOutput && statusOutput.trim()) {
299
+ console.log(RECOVERY.COMMITTING);
300
+ try {
301
+ await git.add([stampPath, wuPath, statusPath, backlogPath]);
302
+ // WU-1383: Set recovery mode env var to bypass stamp existence check in validate.mjs
303
+ // During recovery, stamp and status=done are committed atomically, but pre-commit
304
+ // hooks run mid-commit when both files are staged but validation sees inconsistent state
305
+ process.env.WU_RECOVERY_ID = id;
306
+ try {
307
+ await git.commit(`chore(recovery): complete wu:done metadata for ${id}`);
308
+ }
309
+ finally {
310
+ // Always clean up env var (WU-1383)
311
+ delete process.env.WU_RECOVERY_ID;
312
+ }
313
+ results.commit = { committed: true };
314
+ console.log(RECOVERY.COMMIT_SUCCESS);
315
+ }
316
+ catch (commitError) {
317
+ // Commit failed - rollback all changes (WU-1303)
318
+ console.warn(RECOVERY.COMMIT_FAILED(commitError.message));
319
+ rollbackRecoveryTransaction(initialState, paths);
320
+ throw createError(ErrorCodes.GIT_ERROR, `Recovery commit failed: ${commitError.message}\nFiles rolled back to clean state. Re-run wu:done to retry.`, { originalError: commitError.message, wuId: id });
321
+ }
322
+ }
323
+ else {
324
+ results.commit = { committed: false, reason: 'no_changes' };
325
+ }
326
+ console.log(RECOVERY.MARKERS_VERIFIED);
327
+ console.log(RECOVERY.PROCEEDING_CLEANUP);
328
+ return results;
329
+ }
330
+ catch (error) {
331
+ // WU-1303: Atomic rollback on ANY failure (not just commit)
332
+ // Re-throw if it's already a structured error (from commit phase)
333
+ if (error.code === ErrorCodes.GIT_ERROR) {
334
+ throw error;
335
+ }
336
+ // Rollback file changes for non-commit errors
337
+ console.error(`${LOG_PREFIX.DONE} ${EMOJI.FAILURE} Recovery failed: ${error.message}`);
338
+ rollbackRecoveryTransaction(initialState, paths);
339
+ throw createError(ErrorCodes.RECOVERY_ERROR, `Recovery operation failed: ${error.message}\nFiles rolled back to clean state. Re-run wu:done to retry.`, { originalError: error.message, wuId: id });
340
+ }
341
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * WU Repair Core Module (WU-1826)
3
+ *
4
+ * Unified repair logic for all WU repair operations:
5
+ * - Consistency repair (default mode): detect/repair state inconsistencies
6
+ * - Claim repair (--claim mode): repair missing claim metadata in worktrees
7
+ * - Admin repair (--admin mode): administrative fixes for done WUs
8
+ *
9
+ * This module consolidates logic from:
10
+ * - wu-consistency-checker.mjs (consistency checks)
11
+ * - wu-repair-claim.mjs (claim metadata repair)
12
+ * - wu-admin-repair.mjs (admin fixes)
13
+ * - wu-recovery.mjs (zombie state recovery)
14
+ *
15
+ * @see {@link ../wu-repair.mjs} - Unified CLI interface
16
+ */
17
+ import { checkWUConsistency, checkAllWUConsistency, repairWUInconsistency } from './wu-consistency-checker.js';
18
+ export { checkWUConsistency, checkAllWUConsistency, repairWUInconsistency };
19
+ export { detectZombieState, recoverZombieState, resetWorktreeYAMLForRecovery, getRecoveryMarkerPath, getRecoveryAttemptCount, incrementRecoveryAttempt, clearRecoveryAttempts, shouldEscalateToManualIntervention, MAX_RECOVERY_ATTEMPTS, } from './wu-recovery.js';
20
+ /**
21
+ * Detect worktree path from WU ID using git worktree list
22
+ *
23
+ * @param {string} id - WU ID (e.g., 'WU-1804')
24
+ * @returns {Promise<string|null>} Worktree path or null if not found
25
+ */
26
+ export declare function findWorktreePathForWU(id: any): Promise<string>;
27
+ /**
28
+ * Check claim metadata state for a WU worktree
29
+ *
30
+ * @param {string} id - WU ID
31
+ * @param {string} worktreePath - Path to the worktree
32
+ * @returns {Promise<{valid: boolean, errors: string[], yamlStatus: string|null, stateStoreHasClaim: boolean}>}
33
+ */
34
+ export declare function checkClaimMetadata(id: any, worktreePath: any): Promise<{
35
+ valid: boolean;
36
+ errors: any[];
37
+ yamlStatus: any;
38
+ stateStoreHasClaim: boolean;
39
+ }>;
40
+ /**
41
+ * Repair claim metadata in worktree
42
+ *
43
+ * SAFETY: Only modifies files inside the worktree, never main.
44
+ *
45
+ * @param {string} id - WU ID
46
+ * @param {string} worktreePath - Path to the worktree
47
+ * @param {object} checkResult - Result from checkClaimMetadata
48
+ * @returns {Promise<{success: boolean, repaired: string[], errors: string[]}>}
49
+ */
50
+ export declare function repairClaimMetadata(id: any, worktreePath: any, checkResult: any): Promise<{
51
+ success: boolean;
52
+ repaired: any[];
53
+ errors: any[];
54
+ }>;
55
+ /**
56
+ * Run claim repair mode
57
+ *
58
+ * @param {object} options - CLI options
59
+ * @param {string} options.id - WU ID
60
+ * @param {boolean} [options.check] - Check only, no repair
61
+ * @param {string} [options.worktree] - Override worktree path
62
+ * @returns {Promise<{success: boolean, exitCode: number}>}
63
+ */
64
+ export declare function runClaimRepairMode(options: any): Promise<{
65
+ success: boolean;
66
+ exitCode: number;
67
+ }>;
68
+ /**
69
+ * Apply repairs to WU and track changes for audit
70
+ *
71
+ * @param {object} wu - Original WU object
72
+ * @param {object} opts - CLI options
73
+ * @returns {{ updated: object, changes: string[] }} Updated WU and list of changes
74
+ */
75
+ export declare function applyAdminRepairs(wu: any, opts: any): {
76
+ updated: any;
77
+ changes: any[];
78
+ };
79
+ /**
80
+ * Run admin repair mode
81
+ *
82
+ * @param {object} options - CLI options
83
+ * @param {string} options.id - WU ID
84
+ * @param {string} [options.lane] - New lane assignment
85
+ * @param {string} [options.status] - New status value
86
+ * @param {string} [options.notes] - Notes to add
87
+ * @param {string} [options.initiative] - New initiative reference
88
+ * @returns {Promise<{success: boolean, exitCode: number}>}
89
+ */
90
+ export declare function runAdminRepairMode(options: any): Promise<{
91
+ success: boolean;
92
+ exitCode: number;
93
+ }>;
94
+ /**
95
+ * Repair a single WU for consistency issues
96
+ *
97
+ * @param {string} id - WU ID
98
+ * @param {object} options - CLI options
99
+ * @returns {Promise<{success: boolean, repaired: number, failed: number}>}
100
+ */
101
+ export declare function repairSingleWU(id: any, options: any): Promise<{
102
+ success: boolean;
103
+ repaired: number;
104
+ failed: number;
105
+ }>;
106
+ /**
107
+ * Repair all WUs for consistency issues
108
+ *
109
+ * @param {object} options - CLI options
110
+ * @returns {Promise<{success: boolean, repaired: number, failed: number}>}
111
+ */
112
+ export declare function repairAllWUs(options?: {
113
+ dryRun?: boolean;
114
+ }): Promise<{
115
+ success: boolean;
116
+ repaired: number;
117
+ failed: number;
118
+ }>;
119
+ /**
120
+ * Run consistency repair mode (default)
121
+ *
122
+ * @param {object} options - CLI options
123
+ * @param {string} [options.id] - WU ID to check/repair
124
+ * @param {boolean} [options.all] - Check/repair all WUs
125
+ * @param {boolean} [options.check] - Audit only, no changes
126
+ * @returns {Promise<{success: boolean, exitCode: number}>}
127
+ */
128
+ export declare function runConsistencyRepairMode(options: any): Promise<{
129
+ success: boolean;
130
+ exitCode: number;
131
+ }>;