@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.
- package/LICENSE +190 -0
- package/README.md +119 -0
- package/dist/active-wu-detector.d.ts +33 -0
- package/dist/active-wu-detector.js +106 -0
- package/dist/adapters/filesystem-metrics.adapter.d.ts +108 -0
- package/dist/adapters/filesystem-metrics.adapter.js +519 -0
- package/dist/adapters/terminal-renderer.adapter.d.ts +106 -0
- package/dist/adapters/terminal-renderer.adapter.js +337 -0
- package/dist/arg-parser.d.ts +63 -0
- package/dist/arg-parser.js +560 -0
- package/dist/backlog-editor.d.ts +98 -0
- package/dist/backlog-editor.js +179 -0
- package/dist/backlog-generator.d.ts +111 -0
- package/dist/backlog-generator.js +381 -0
- package/dist/backlog-parser.d.ts +45 -0
- package/dist/backlog-parser.js +102 -0
- package/dist/backlog-sync-validator.d.ts +78 -0
- package/dist/backlog-sync-validator.js +294 -0
- package/dist/branch-drift.d.ts +34 -0
- package/dist/branch-drift.js +51 -0
- package/dist/cleanup-install-config.d.ts +33 -0
- package/dist/cleanup-install-config.js +37 -0
- package/dist/cleanup-lock.d.ts +139 -0
- package/dist/cleanup-lock.js +313 -0
- package/dist/code-path-validator.d.ts +146 -0
- package/dist/code-path-validator.js +537 -0
- package/dist/code-paths-overlap.d.ts +55 -0
- package/dist/code-paths-overlap.js +245 -0
- package/dist/commands-logger.d.ts +77 -0
- package/dist/commands-logger.js +254 -0
- package/dist/commit-message-utils.d.ts +25 -0
- package/dist/commit-message-utils.js +41 -0
- package/dist/compliance-parser.d.ts +150 -0
- package/dist/compliance-parser.js +507 -0
- package/dist/constants/backlog-patterns.d.ts +20 -0
- package/dist/constants/backlog-patterns.js +23 -0
- package/dist/constants/dora-constants.d.ts +49 -0
- package/dist/constants/dora-constants.js +53 -0
- package/dist/constants/gate-constants.d.ts +15 -0
- package/dist/constants/gate-constants.js +15 -0
- package/dist/constants/linter-constants.d.ts +16 -0
- package/dist/constants/linter-constants.js +16 -0
- package/dist/constants/tokenizer-constants.d.ts +15 -0
- package/dist/constants/tokenizer-constants.js +15 -0
- package/dist/core/scope-checker.d.ts +97 -0
- package/dist/core/scope-checker.js +163 -0
- package/dist/core/tool-runner.d.ts +161 -0
- package/dist/core/tool-runner.js +393 -0
- package/dist/core/tool.constants.d.ts +105 -0
- package/dist/core/tool.constants.js +101 -0
- package/dist/core/tool.schemas.d.ts +226 -0
- package/dist/core/tool.schemas.js +226 -0
- package/dist/core/worktree-guard.d.ts +130 -0
- package/dist/core/worktree-guard.js +242 -0
- package/dist/coverage-gate.d.ts +108 -0
- package/dist/coverage-gate.js +196 -0
- package/dist/date-utils.d.ts +75 -0
- package/dist/date-utils.js +140 -0
- package/dist/dependency-graph.d.ts +142 -0
- package/dist/dependency-graph.js +550 -0
- package/dist/dependency-guard.d.ts +54 -0
- package/dist/dependency-guard.js +142 -0
- package/dist/dependency-validator.d.ts +105 -0
- package/dist/dependency-validator.js +154 -0
- package/dist/docs-path-validator.d.ts +36 -0
- package/dist/docs-path-validator.js +95 -0
- package/dist/domain/orchestration.constants.d.ts +99 -0
- package/dist/domain/orchestration.constants.js +97 -0
- package/dist/domain/orchestration.schemas.d.ts +280 -0
- package/dist/domain/orchestration.schemas.js +211 -0
- package/dist/domain/orchestration.types.d.ts +133 -0
- package/dist/domain/orchestration.types.js +12 -0
- package/dist/error-handler.d.ts +116 -0
- package/dist/error-handler.js +136 -0
- package/dist/file-classifiers.d.ts +62 -0
- package/dist/file-classifiers.js +108 -0
- package/dist/gates-agent-mode.d.ts +81 -0
- package/dist/gates-agent-mode.js +94 -0
- package/dist/generate-traceability.d.ts +107 -0
- package/dist/generate-traceability.js +411 -0
- package/dist/git-adapter.d.ts +395 -0
- package/dist/git-adapter.js +649 -0
- package/dist/git-staged-validator.d.ts +32 -0
- package/dist/git-staged-validator.js +48 -0
- package/dist/hardcoded-strings.d.ts +61 -0
- package/dist/hardcoded-strings.js +270 -0
- package/dist/incremental-lint.d.ts +78 -0
- package/dist/incremental-lint.js +129 -0
- package/dist/incremental-test.d.ts +39 -0
- package/dist/incremental-test.js +61 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +61 -0
- package/dist/invariants/check-automated-tests.d.ts +50 -0
- package/dist/invariants/check-automated-tests.js +166 -0
- package/dist/invariants-runner.d.ts +103 -0
- package/dist/invariants-runner.js +527 -0
- package/dist/lane-checker.d.ts +50 -0
- package/dist/lane-checker.js +319 -0
- package/dist/lane-inference.d.ts +39 -0
- package/dist/lane-inference.js +195 -0
- package/dist/lane-lock.d.ts +211 -0
- package/dist/lane-lock.js +474 -0
- package/dist/lane-validator.d.ts +48 -0
- package/dist/lane-validator.js +114 -0
- package/dist/logs-lib.d.ts +104 -0
- package/dist/logs-lib.js +207 -0
- package/dist/lumenflow-config-schema.d.ts +272 -0
- package/dist/lumenflow-config-schema.js +207 -0
- package/dist/lumenflow-config.d.ts +95 -0
- package/dist/lumenflow-config.js +236 -0
- package/dist/manual-test-validator.d.ts +80 -0
- package/dist/manual-test-validator.js +200 -0
- package/dist/merge-lock.d.ts +115 -0
- package/dist/merge-lock.js +251 -0
- package/dist/micro-worktree.d.ts +159 -0
- package/dist/micro-worktree.js +427 -0
- package/dist/migration-deployer.d.ts +69 -0
- package/dist/migration-deployer.js +151 -0
- package/dist/orchestration-advisory-loader.d.ts +28 -0
- package/dist/orchestration-advisory-loader.js +87 -0
- package/dist/orchestration-advisory.d.ts +58 -0
- package/dist/orchestration-advisory.js +94 -0
- package/dist/orchestration-di.d.ts +48 -0
- package/dist/orchestration-di.js +57 -0
- package/dist/orchestration-rules.d.ts +57 -0
- package/dist/orchestration-rules.js +201 -0
- package/dist/orphan-detector.d.ts +131 -0
- package/dist/orphan-detector.js +226 -0
- package/dist/path-classifiers.d.ts +57 -0
- package/dist/path-classifiers.js +93 -0
- package/dist/piped-command-detector.d.ts +34 -0
- package/dist/piped-command-detector.js +64 -0
- package/dist/ports/dashboard-renderer.port.d.ts +112 -0
- package/dist/ports/dashboard-renderer.port.js +25 -0
- package/dist/ports/metrics-collector.port.d.ts +132 -0
- package/dist/ports/metrics-collector.port.js +26 -0
- package/dist/process-detector.d.ts +84 -0
- package/dist/process-detector.js +172 -0
- package/dist/prompt-linter.d.ts +72 -0
- package/dist/prompt-linter.js +312 -0
- package/dist/prompt-monitor.d.ts +15 -0
- package/dist/prompt-monitor.js +205 -0
- package/dist/rebase-artifact-cleanup.d.ts +145 -0
- package/dist/rebase-artifact-cleanup.js +433 -0
- package/dist/retry-strategy.d.ts +189 -0
- package/dist/retry-strategy.js +283 -0
- package/dist/risk-detector.d.ts +108 -0
- package/dist/risk-detector.js +252 -0
- package/dist/rollback-utils.d.ts +76 -0
- package/dist/rollback-utils.js +104 -0
- package/dist/section-headings.d.ts +43 -0
- package/dist/section-headings.js +49 -0
- package/dist/spawn-escalation.d.ts +90 -0
- package/dist/spawn-escalation.js +253 -0
- package/dist/spawn-monitor.d.ts +229 -0
- package/dist/spawn-monitor.js +672 -0
- package/dist/spawn-recovery.d.ts +82 -0
- package/dist/spawn-recovery.js +298 -0
- package/dist/spawn-registry-schema.d.ts +98 -0
- package/dist/spawn-registry-schema.js +108 -0
- package/dist/spawn-registry-store.d.ts +146 -0
- package/dist/spawn-registry-store.js +273 -0
- package/dist/spawn-tree.d.ts +121 -0
- package/dist/spawn-tree.js +285 -0
- package/dist/stamp-status-validator.d.ts +84 -0
- package/dist/stamp-status-validator.js +134 -0
- package/dist/stamp-utils.d.ts +100 -0
- package/dist/stamp-utils.js +229 -0
- package/dist/state-machine.d.ts +26 -0
- package/dist/state-machine.js +83 -0
- package/dist/system-map-validator.d.ts +80 -0
- package/dist/system-map-validator.js +272 -0
- package/dist/telemetry.d.ts +80 -0
- package/dist/telemetry.js +213 -0
- package/dist/token-counter.d.ts +51 -0
- package/dist/token-counter.js +145 -0
- package/dist/usecases/get-dashboard-data.usecase.d.ts +52 -0
- package/dist/usecases/get-dashboard-data.usecase.js +61 -0
- package/dist/usecases/get-suggestions.usecase.d.ts +100 -0
- package/dist/usecases/get-suggestions.usecase.js +153 -0
- package/dist/user-normalizer.d.ts +41 -0
- package/dist/user-normalizer.js +141 -0
- package/dist/validators/phi-constants.d.ts +97 -0
- package/dist/validators/phi-constants.js +152 -0
- package/dist/validators/phi-scanner.d.ts +58 -0
- package/dist/validators/phi-scanner.js +215 -0
- package/dist/worktree-ownership.d.ts +50 -0
- package/dist/worktree-ownership.js +74 -0
- package/dist/worktree-scanner.d.ts +103 -0
- package/dist/worktree-scanner.js +168 -0
- package/dist/worktree-symlink.d.ts +99 -0
- package/dist/worktree-symlink.js +359 -0
- package/dist/wu-backlog-updater.d.ts +17 -0
- package/dist/wu-backlog-updater.js +37 -0
- package/dist/wu-checkpoint.d.ts +124 -0
- package/dist/wu-checkpoint.js +233 -0
- package/dist/wu-claim-helpers.d.ts +26 -0
- package/dist/wu-claim-helpers.js +63 -0
- package/dist/wu-claim-resume.d.ts +106 -0
- package/dist/wu-claim-resume.js +276 -0
- package/dist/wu-consistency-checker.d.ts +95 -0
- package/dist/wu-consistency-checker.js +567 -0
- package/dist/wu-constants.d.ts +1275 -0
- package/dist/wu-constants.js +1382 -0
- package/dist/wu-create-validators.d.ts +42 -0
- package/dist/wu-create-validators.js +93 -0
- package/dist/wu-done-branch-only.d.ts +63 -0
- package/dist/wu-done-branch-only.js +191 -0
- package/dist/wu-done-messages.d.ts +119 -0
- package/dist/wu-done-messages.js +185 -0
- package/dist/wu-done-pr.d.ts +72 -0
- package/dist/wu-done-pr.js +174 -0
- package/dist/wu-done-retry-helpers.d.ts +85 -0
- package/dist/wu-done-retry-helpers.js +172 -0
- package/dist/wu-done-ui.d.ts +37 -0
- package/dist/wu-done-ui.js +69 -0
- package/dist/wu-done-validators.d.ts +411 -0
- package/dist/wu-done-validators.js +1229 -0
- package/dist/wu-done-worktree.d.ts +182 -0
- package/dist/wu-done-worktree.js +1097 -0
- package/dist/wu-helpers.d.ts +128 -0
- package/dist/wu-helpers.js +248 -0
- package/dist/wu-lint.d.ts +70 -0
- package/dist/wu-lint.js +234 -0
- package/dist/wu-paths.d.ts +171 -0
- package/dist/wu-paths.js +178 -0
- package/dist/wu-preflight-validators.d.ts +86 -0
- package/dist/wu-preflight-validators.js +251 -0
- package/dist/wu-recovery.d.ts +138 -0
- package/dist/wu-recovery.js +341 -0
- package/dist/wu-repair-core.d.ts +131 -0
- package/dist/wu-repair-core.js +669 -0
- package/dist/wu-schema-normalization.d.ts +17 -0
- package/dist/wu-schema-normalization.js +82 -0
- package/dist/wu-schema.d.ts +793 -0
- package/dist/wu-schema.js +881 -0
- package/dist/wu-spawn-helpers.d.ts +121 -0
- package/dist/wu-spawn-helpers.js +271 -0
- package/dist/wu-spawn.d.ts +158 -0
- package/dist/wu-spawn.js +1306 -0
- package/dist/wu-state-schema.d.ts +213 -0
- package/dist/wu-state-schema.js +156 -0
- package/dist/wu-state-store.d.ts +264 -0
- package/dist/wu-state-store.js +691 -0
- package/dist/wu-status-transition.d.ts +63 -0
- package/dist/wu-status-transition.js +382 -0
- package/dist/wu-status-updater.d.ts +25 -0
- package/dist/wu-status-updater.js +116 -0
- package/dist/wu-transaction-collectors.d.ts +116 -0
- package/dist/wu-transaction-collectors.js +272 -0
- package/dist/wu-transaction.d.ts +170 -0
- package/dist/wu-transaction.js +273 -0
- package/dist/wu-validation-constants.d.ts +60 -0
- package/dist/wu-validation-constants.js +66 -0
- package/dist/wu-validation.d.ts +118 -0
- package/dist/wu-validation.js +243 -0
- package/dist/wu-validator.d.ts +62 -0
- package/dist/wu-validator.js +325 -0
- package/dist/wu-yaml-fixer.d.ts +97 -0
- package/dist/wu-yaml-fixer.js +264 -0
- package/dist/wu-yaml.d.ts +86 -0
- package/dist/wu-yaml.js +222 -0
- package/package.json +114 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rebase Artifact Cleanup
|
|
3
|
+
*
|
|
4
|
+
* Detects and cleans up completion artifacts (stamps, status=done)
|
|
5
|
+
* that appear in worktree after rebasing from main.
|
|
6
|
+
*
|
|
7
|
+
* This prevents contradictory state where an in_progress WU has
|
|
8
|
+
* completion markers from a previous completion cycle on main.
|
|
9
|
+
*
|
|
10
|
+
* Part of WU-1371: Post-rebase artifact cleanup
|
|
11
|
+
* WU-1449: Extended to handle backlog/status duplicates after rebase
|
|
12
|
+
*
|
|
13
|
+
* @see {@link tools/wu-done.mjs} - Creates completion artifacts
|
|
14
|
+
* @see {@link tools/lib/stamp-utils.mjs} - Stamp file utilities
|
|
15
|
+
* @see {@link tools/lib/wu-recovery.mjs} - Related zombie state handling
|
|
16
|
+
*/
|
|
17
|
+
import { readFile, writeFile, unlink, access } from 'node:fs/promises';
|
|
18
|
+
import { existsSync } from 'node:fs';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
import yaml from 'js-yaml';
|
|
21
|
+
import { WU_PATHS } from './wu-paths.js';
|
|
22
|
+
import { WU_STATUS, LOG_PREFIX, EMOJI, YAML_OPTIONS, BACKLOG_SECTIONS, STATUS_SECTIONS, REMOTES, BRANCHES, } from './wu-constants.js';
|
|
23
|
+
import { findSectionBounds, removeBulletFromSection } from './backlog-editor.js';
|
|
24
|
+
/** @constant {string} FRONTMATTER_DELIMITER - YAML frontmatter delimiter */
|
|
25
|
+
const FRONTMATTER_DELIMITER = '---';
|
|
26
|
+
/**
|
|
27
|
+
* Check if a file exists (async)
|
|
28
|
+
* @param {string} filePath - Path to check
|
|
29
|
+
* @returns {Promise<boolean>} True if file exists
|
|
30
|
+
*/
|
|
31
|
+
async function fileExists(filePath) {
|
|
32
|
+
try {
|
|
33
|
+
await access(filePath);
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Check if a file exists on origin/main using git show
|
|
42
|
+
* WU-1817: Used to verify artifacts are truly rebased from main
|
|
43
|
+
*
|
|
44
|
+
* @param {object} gitAdapter - Git adapter instance with raw() method
|
|
45
|
+
* @param {string} relativePath - Path relative to repo root
|
|
46
|
+
* @returns {Promise<boolean>} True if file exists on origin/main
|
|
47
|
+
*/
|
|
48
|
+
async function fileExistsOnMain(gitAdapter, relativePath) {
|
|
49
|
+
try {
|
|
50
|
+
await gitAdapter.raw(['show', `${REMOTES.ORIGIN}/${BRANCHES.MAIN}:${relativePath}`]);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Check if YAML on origin/main has status=done
|
|
59
|
+
* WU-1817: Used to verify done status is truly rebased from main
|
|
60
|
+
*
|
|
61
|
+
* @param {object} gitAdapter - Git adapter instance with raw() method
|
|
62
|
+
* @param {string} wuId - WU ID (e.g., 'WU-1817')
|
|
63
|
+
* @returns {Promise<boolean>} True if YAML on main has status=done
|
|
64
|
+
*/
|
|
65
|
+
async function yamlIsDoneOnMain(gitAdapter, wuId) {
|
|
66
|
+
try {
|
|
67
|
+
const content = await gitAdapter.raw([
|
|
68
|
+
'show',
|
|
69
|
+
`${REMOTES.ORIGIN}/${BRANCHES.MAIN}:${WU_PATHS.WU(wuId)}`,
|
|
70
|
+
]);
|
|
71
|
+
const doc = yaml.load(content);
|
|
72
|
+
return doc && doc.status === WU_STATUS.DONE;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Read markdown file and separate frontmatter from content
|
|
80
|
+
* Simplified version that doesn't require gray-matter (which has js-yaml compatibility issues)
|
|
81
|
+
*
|
|
82
|
+
* @param {string} filePath - Path to file
|
|
83
|
+
* @returns {{frontmatter: string, lines: string[]}} Frontmatter and content lines
|
|
84
|
+
*/
|
|
85
|
+
function readMarkdownFile(filePath) {
|
|
86
|
+
const { readFileSync } = require('node:fs');
|
|
87
|
+
const raw = readFileSync(filePath, { encoding: 'utf-8' });
|
|
88
|
+
const allLines = raw.split('\n');
|
|
89
|
+
// Check for frontmatter (starts with ---)
|
|
90
|
+
if (allLines[0]?.trim() === FRONTMATTER_DELIMITER) {
|
|
91
|
+
// Find closing ---
|
|
92
|
+
let endIdx = -1;
|
|
93
|
+
for (let i = 1; i < allLines.length; i++) {
|
|
94
|
+
if (allLines[i]?.trim() === FRONTMATTER_DELIMITER) {
|
|
95
|
+
endIdx = i;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (endIdx > 0) {
|
|
100
|
+
// Extract frontmatter and content
|
|
101
|
+
const frontmatterLines = allLines.slice(0, endIdx + 1);
|
|
102
|
+
const contentLines = allLines.slice(endIdx + 1);
|
|
103
|
+
return {
|
|
104
|
+
frontmatter: `${frontmatterLines.join('\n')}\n`,
|
|
105
|
+
lines: contentLines,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// No frontmatter
|
|
110
|
+
return {
|
|
111
|
+
frontmatter: '',
|
|
112
|
+
lines: allLines,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Write markdown file with frontmatter and content
|
|
117
|
+
*
|
|
118
|
+
* @param {string} filePath - Path to file
|
|
119
|
+
* @param {string} frontmatter - Frontmatter text (including --- markers)
|
|
120
|
+
* @param {string[]} lines - Content lines
|
|
121
|
+
*/
|
|
122
|
+
function writeMarkdownFile(filePath, frontmatter, lines) {
|
|
123
|
+
const { writeFileSync } = require('node:fs');
|
|
124
|
+
const content = frontmatter + lines.join('\n');
|
|
125
|
+
writeFileSync(filePath, content, { encoding: 'utf-8' });
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Detect rebased completion artifacts in a worktree
|
|
129
|
+
*
|
|
130
|
+
* WU-1817: Now verifies artifacts exist on origin/main before flagging.
|
|
131
|
+
* Only artifacts that exist on BOTH worktree AND origin/main are true
|
|
132
|
+
* rebased artifacts. Artifacts that exist only locally (created by the
|
|
133
|
+
* lane branch itself) should NOT be cleaned - this was the WU-1816 bug.
|
|
134
|
+
*
|
|
135
|
+
* Checks for:
|
|
136
|
+
* 1. Stamp files (.beacon/stamps/WU-{id}.done) that exist on origin/main
|
|
137
|
+
* 2. WU YAML with status=done that also has status=done on origin/main
|
|
138
|
+
*
|
|
139
|
+
* @param {string} worktreePath - Path to the worktree directory
|
|
140
|
+
* @param {string} wuId - WU ID (e.g., 'WU-1371')
|
|
141
|
+
* @param {object} gitAdapter - Git adapter instance with raw() method
|
|
142
|
+
* @returns {Promise<object>} Detection result
|
|
143
|
+
* @returns {string[]} result.stamps - Array of detected stamp file paths (only if on origin/main)
|
|
144
|
+
* @returns {boolean} result.yamlStatusDone - True if YAML has status=done AND origin/main has done
|
|
145
|
+
* @returns {boolean} result.hasArtifacts - True if any rebased artifacts detected
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* const result = await detectRebasedArtifacts(worktreePath, wuId, gitAdapter);
|
|
149
|
+
* if (result.hasArtifacts) {
|
|
150
|
+
* console.log('Found rebased artifacts, cleaning up...');
|
|
151
|
+
* }
|
|
152
|
+
*/
|
|
153
|
+
export async function detectRebasedArtifacts(worktreePath, wuId, gitAdapter) {
|
|
154
|
+
const stamps = [];
|
|
155
|
+
let yamlStatusDone = false;
|
|
156
|
+
// Check for stamp file in worktree
|
|
157
|
+
const stampPath = join(worktreePath, WU_PATHS.STAMP(wuId));
|
|
158
|
+
const localStampExists = await fileExists(stampPath);
|
|
159
|
+
// Check YAML status in worktree
|
|
160
|
+
const wuYamlPath = join(worktreePath, WU_PATHS.WU(wuId));
|
|
161
|
+
let localYamlDone = false;
|
|
162
|
+
if (await fileExists(wuYamlPath)) {
|
|
163
|
+
try {
|
|
164
|
+
const content = await readFile(wuYamlPath, { encoding: 'utf-8' });
|
|
165
|
+
const doc = yaml.load(content);
|
|
166
|
+
if (doc && doc.status === WU_STATUS.DONE) {
|
|
167
|
+
localYamlDone = true;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
// YAML read error - treat as no artifact (will be caught elsewhere)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// WU-1817: Verify artifacts also exist on origin/main
|
|
175
|
+
// Only flag as rebased artifact if exists on BOTH worktree AND main
|
|
176
|
+
const stampOnMain = localStampExists
|
|
177
|
+
? await fileExistsOnMain(gitAdapter, WU_PATHS.STAMP(wuId))
|
|
178
|
+
: false;
|
|
179
|
+
const yamlDoneOnMain = localYamlDone ? await yamlIsDoneOnMain(gitAdapter, wuId) : false;
|
|
180
|
+
// Only include artifacts that exist on both
|
|
181
|
+
if (stampOnMain) {
|
|
182
|
+
stamps.push(stampPath);
|
|
183
|
+
}
|
|
184
|
+
yamlStatusDone = yamlDoneOnMain;
|
|
185
|
+
const hasArtifacts = stamps.length > 0 || yamlStatusDone;
|
|
186
|
+
return {
|
|
187
|
+
stamps,
|
|
188
|
+
yamlStatusDone,
|
|
189
|
+
hasArtifacts,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Clean up rebased completion artifacts from a worktree
|
|
194
|
+
*
|
|
195
|
+
* Actions:
|
|
196
|
+
* 1. Remove stamp files that shouldn't exist
|
|
197
|
+
* 2. Reset YAML status from done to in_progress
|
|
198
|
+
* 3. Remove locked and completed_at fields from YAML
|
|
199
|
+
* 4. Log warnings explaining cleanup actions
|
|
200
|
+
*
|
|
201
|
+
* Idempotent: Safe to call multiple times, won't throw if artifacts don't exist.
|
|
202
|
+
*
|
|
203
|
+
* @param {string} worktreePath - Path to the worktree directory
|
|
204
|
+
* @param {string} wuId - WU ID (e.g., 'WU-1371')
|
|
205
|
+
* @returns {Promise<object>} Cleanup result
|
|
206
|
+
* @returns {string[]} result.stampsCleaned - WU IDs whose stamps were removed
|
|
207
|
+
* @returns {boolean} result.yamlReset - True if YAML status was reset
|
|
208
|
+
* @returns {string[]} result.errors - Any errors encountered (non-fatal)
|
|
209
|
+
* @returns {boolean} result.cleaned - True if any cleanup was performed
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* const result = await cleanupRebasedArtifacts(worktreePath, wuId);
|
|
213
|
+
* if (result.cleaned) {
|
|
214
|
+
* console.log('Cleaned rebased artifacts:', result);
|
|
215
|
+
* }
|
|
216
|
+
*/
|
|
217
|
+
export async function cleanupRebasedArtifacts(worktreePath, wuId) {
|
|
218
|
+
const stampsCleaned = [];
|
|
219
|
+
let yamlReset = false;
|
|
220
|
+
const errors = [];
|
|
221
|
+
// Clean stamp file
|
|
222
|
+
const stampPath = join(worktreePath, WU_PATHS.STAMP(wuId));
|
|
223
|
+
try {
|
|
224
|
+
if (await fileExists(stampPath)) {
|
|
225
|
+
await unlink(stampPath);
|
|
226
|
+
stampsCleaned.push(wuId);
|
|
227
|
+
console.log(LOG_PREFIX.CLEANUP, `${EMOJI.WARNING} Removed rebased stamp file for ${wuId} (artifact from main rebase)`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
const stampErrMessage = error instanceof Error ? error.message : String(error);
|
|
232
|
+
errors.push(`Failed to remove stamp: ${stampErrMessage}`);
|
|
233
|
+
}
|
|
234
|
+
// Reset YAML status
|
|
235
|
+
const wuYamlPath = join(worktreePath, WU_PATHS.WU(wuId));
|
|
236
|
+
try {
|
|
237
|
+
if (await fileExists(wuYamlPath)) {
|
|
238
|
+
const content = await readFile(wuYamlPath, { encoding: 'utf-8' });
|
|
239
|
+
const doc = yaml.load(content);
|
|
240
|
+
if (doc && doc.status === WU_STATUS.DONE) {
|
|
241
|
+
// Reset status
|
|
242
|
+
doc.status = WU_STATUS.IN_PROGRESS;
|
|
243
|
+
// Remove completion fields
|
|
244
|
+
delete doc.locked;
|
|
245
|
+
delete doc.completed_at;
|
|
246
|
+
// Write back
|
|
247
|
+
const updatedContent = yaml.dump(doc, { lineWidth: YAML_OPTIONS.LINE_WIDTH });
|
|
248
|
+
await writeFile(wuYamlPath, updatedContent, { encoding: 'utf-8' });
|
|
249
|
+
yamlReset = true;
|
|
250
|
+
console.log(LOG_PREFIX.CLEANUP, `${EMOJI.WARNING} Reset YAML status from done to in_progress for ${wuId} (artifact from main rebase)`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
256
|
+
errors.push(`Failed to reset YAML status: ${message}`);
|
|
257
|
+
}
|
|
258
|
+
// WU-1449: Also clean backlog/status duplicates
|
|
259
|
+
const backlogDedup = await deduplicateBacklogAfterRebase(worktreePath, wuId);
|
|
260
|
+
const cleaned = stampsCleaned.length > 0 || yamlReset || backlogDedup.cleaned;
|
|
261
|
+
return {
|
|
262
|
+
stampsCleaned,
|
|
263
|
+
yamlReset,
|
|
264
|
+
backlogCleaned: backlogDedup.backlogCleaned,
|
|
265
|
+
statusCleaned: backlogDedup.statusCleaned,
|
|
266
|
+
errors: [...errors, ...backlogDedup.errors],
|
|
267
|
+
cleaned,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Detect WU duplicates in backlog/status files after rebase
|
|
272
|
+
*
|
|
273
|
+
* Checks if a WU appears in both:
|
|
274
|
+
* - "In Progress" AND "Done" sections of backlog.md
|
|
275
|
+
* - "In Progress" AND "Completed" sections of status.md
|
|
276
|
+
*
|
|
277
|
+
* This state occurs when main advanced with WU completion,
|
|
278
|
+
* then rebase merged main's "Done" state into the worktree
|
|
279
|
+
* while the worktree already had the WU in "In Progress".
|
|
280
|
+
*
|
|
281
|
+
* Part of WU-1449: Extend rebase cleanup to remove backlog/status duplicates
|
|
282
|
+
*
|
|
283
|
+
* @param {string} worktreePath - Path to the worktree directory
|
|
284
|
+
* @param {string} wuId - WU ID (e.g., 'WU-1449')
|
|
285
|
+
* @returns {Promise<object>} Detection result
|
|
286
|
+
* @returns {boolean} result.backlogDuplicate - True if WU in both In Progress and Done in backlog.md
|
|
287
|
+
* @returns {boolean} result.statusDuplicate - True if WU in both In Progress and Completed in status.md
|
|
288
|
+
* @returns {boolean} result.hasDuplicates - True if any duplicates detected
|
|
289
|
+
*
|
|
290
|
+
* @example
|
|
291
|
+
* const result = await detectBacklogDuplicates(worktreePath, wuId);
|
|
292
|
+
* if (result.hasDuplicates) {
|
|
293
|
+
* console.log('Found backlog duplicates, cleaning up...');
|
|
294
|
+
* }
|
|
295
|
+
*/
|
|
296
|
+
export async function detectBacklogDuplicates(worktreePath, wuId) {
|
|
297
|
+
let backlogDuplicate = false;
|
|
298
|
+
let statusDuplicate = false;
|
|
299
|
+
// Check backlog.md for duplicate (WU in both In Progress and Done)
|
|
300
|
+
const backlogPath = join(worktreePath, WU_PATHS.BACKLOG());
|
|
301
|
+
if (existsSync(backlogPath)) {
|
|
302
|
+
try {
|
|
303
|
+
const { lines } = readMarkdownFile(backlogPath);
|
|
304
|
+
const inProgressBounds = findSectionBounds(lines, BACKLOG_SECTIONS.IN_PROGRESS);
|
|
305
|
+
const doneBounds = findSectionBounds(lines, BACKLOG_SECTIONS.DONE);
|
|
306
|
+
if (inProgressBounds && doneBounds) {
|
|
307
|
+
const inProgressSection = lines.slice(inProgressBounds.start, inProgressBounds.end);
|
|
308
|
+
const doneSection = lines.slice(doneBounds.start, doneBounds.end);
|
|
309
|
+
const inProgressHasWU = inProgressSection.some((line) => line.includes(wuId));
|
|
310
|
+
const doneHasWU = doneSection.some((line) => line.includes(wuId));
|
|
311
|
+
backlogDuplicate = inProgressHasWU && doneHasWU;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
// File read error - treat as no duplicate (will be caught elsewhere)
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// Check status.md for duplicate (WU in both In Progress and Completed)
|
|
319
|
+
const statusPath = join(worktreePath, WU_PATHS.STATUS());
|
|
320
|
+
if (existsSync(statusPath)) {
|
|
321
|
+
try {
|
|
322
|
+
const { lines } = readMarkdownFile(statusPath);
|
|
323
|
+
const inProgressBounds = findSectionBounds(lines, STATUS_SECTIONS.IN_PROGRESS);
|
|
324
|
+
const completedBounds = findSectionBounds(lines, STATUS_SECTIONS.COMPLETED);
|
|
325
|
+
if (inProgressBounds && completedBounds) {
|
|
326
|
+
const inProgressSection = lines.slice(inProgressBounds.start, inProgressBounds.end);
|
|
327
|
+
const completedSection = lines.slice(completedBounds.start, completedBounds.end);
|
|
328
|
+
const inProgressHasWU = inProgressSection.some((line) => line.includes(wuId));
|
|
329
|
+
const completedHasWU = completedSection.some((line) => line.includes(wuId));
|
|
330
|
+
statusDuplicate = inProgressHasWU && completedHasWU;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
// File read error - treat as no duplicate (will be caught elsewhere)
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const hasDuplicates = backlogDuplicate || statusDuplicate;
|
|
338
|
+
return {
|
|
339
|
+
backlogDuplicate,
|
|
340
|
+
statusDuplicate,
|
|
341
|
+
hasDuplicates,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Clean duplicates from a single markdown file
|
|
346
|
+
*
|
|
347
|
+
* Helper function to reduce cognitive complexity of deduplicateBacklogAfterRebase.
|
|
348
|
+
*
|
|
349
|
+
* @param {string} filePath - Path to the markdown file
|
|
350
|
+
* @param {string} wuId - WU ID to check for duplicates
|
|
351
|
+
* @param {string} inProgressSection - Section name for in-progress items
|
|
352
|
+
* @param {string} completedSection - Section name for completed items
|
|
353
|
+
* @param {string} fileLabel - Label for log messages (e.g., 'backlog.md')
|
|
354
|
+
* @param {string} completedLabel - Label for completed section (e.g., 'Done' or 'Completed')
|
|
355
|
+
* @returns {{cleaned: boolean, error: string|null}} Result
|
|
356
|
+
*/
|
|
357
|
+
function cleanDuplicatesFromFile(filePath, wuId, inProgressSection, completedSection, fileLabel, completedLabel) {
|
|
358
|
+
if (!existsSync(filePath)) {
|
|
359
|
+
return { cleaned: false, error: null };
|
|
360
|
+
}
|
|
361
|
+
try {
|
|
362
|
+
const { frontmatter, lines } = readMarkdownFile(filePath);
|
|
363
|
+
const inProgressBounds = findSectionBounds(lines, inProgressSection);
|
|
364
|
+
const completedBounds = findSectionBounds(lines, completedSection);
|
|
365
|
+
if (!inProgressBounds || !completedBounds) {
|
|
366
|
+
return { cleaned: false, error: null };
|
|
367
|
+
}
|
|
368
|
+
const inProgressLines = lines.slice(inProgressBounds.start, inProgressBounds.end);
|
|
369
|
+
const completedLines = lines.slice(completedBounds.start, completedBounds.end);
|
|
370
|
+
const inProgressHasWU = inProgressLines.some((line) => line.includes(wuId));
|
|
371
|
+
const completedHasWU = completedLines.some((line) => line.includes(wuId));
|
|
372
|
+
// Only clean if WU is in BOTH sections (duplicate state)
|
|
373
|
+
if (!inProgressHasWU || !completedHasWU) {
|
|
374
|
+
return { cleaned: false, error: null };
|
|
375
|
+
}
|
|
376
|
+
removeBulletFromSection(lines, inProgressBounds.start, inProgressBounds.end, wuId);
|
|
377
|
+
writeMarkdownFile(filePath, frontmatter, lines);
|
|
378
|
+
console.log(LOG_PREFIX.CLEANUP, `${EMOJI.WARNING} Removed ${wuId} from In Progress section in ${fileLabel} (already in ${completedLabel} after rebase)`);
|
|
379
|
+
return { cleaned: true, error: null };
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
return { cleaned: false, error: `Failed to clean ${fileLabel}: ${error.message}` };
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Remove WU from In Progress sections when already in Done/Completed after rebase
|
|
387
|
+
*
|
|
388
|
+
* This handles the specific case where:
|
|
389
|
+
* 1. WU is completing (wu:done in progress)
|
|
390
|
+
* 2. Auto-rebase pulls main's completion state
|
|
391
|
+
* 3. WU now appears in BOTH In Progress AND Done sections
|
|
392
|
+
* 4. This function removes the duplicate from In Progress (keeps Done)
|
|
393
|
+
*
|
|
394
|
+
* Applies to both backlog.md and status.md.
|
|
395
|
+
* Idempotent: Safe to call multiple times.
|
|
396
|
+
*
|
|
397
|
+
* Part of WU-1449: Extend rebase cleanup to remove backlog/status duplicates
|
|
398
|
+
*
|
|
399
|
+
* @param {string} worktreePath - Path to the worktree directory
|
|
400
|
+
* @param {string} wuId - WU ID (e.g., 'WU-1449')
|
|
401
|
+
* @returns {Promise<object>} Cleanup result
|
|
402
|
+
* @returns {boolean} result.backlogCleaned - True if WU removed from backlog.md In Progress
|
|
403
|
+
* @returns {boolean} result.statusCleaned - True if WU removed from status.md In Progress
|
|
404
|
+
* @returns {boolean} result.cleaned - True if any cleanup was performed
|
|
405
|
+
* @returns {string[]} result.errors - Any errors encountered (non-fatal)
|
|
406
|
+
*
|
|
407
|
+
* @example
|
|
408
|
+
* const result = await deduplicateBacklogAfterRebase(worktreePath, wuId);
|
|
409
|
+
* if (result.cleaned) {
|
|
410
|
+
* console.log('Cleaned backlog duplicates:', result);
|
|
411
|
+
* }
|
|
412
|
+
*/
|
|
413
|
+
export async function deduplicateBacklogAfterRebase(worktreePath, wuId) {
|
|
414
|
+
const errors = [];
|
|
415
|
+
// Clean backlog.md duplicates
|
|
416
|
+
const backlogPath = join(worktreePath, WU_PATHS.BACKLOG());
|
|
417
|
+
const backlogResult = cleanDuplicatesFromFile(backlogPath, wuId, BACKLOG_SECTIONS.IN_PROGRESS, BACKLOG_SECTIONS.DONE, 'backlog.md', 'Done');
|
|
418
|
+
if (backlogResult.error) {
|
|
419
|
+
errors.push(backlogResult.error);
|
|
420
|
+
}
|
|
421
|
+
// Clean status.md duplicates
|
|
422
|
+
const statusPath = join(worktreePath, WU_PATHS.STATUS());
|
|
423
|
+
const statusResult = cleanDuplicatesFromFile(statusPath, wuId, STATUS_SECTIONS.IN_PROGRESS, STATUS_SECTIONS.COMPLETED, 'status.md', 'Completed');
|
|
424
|
+
if (statusResult.error) {
|
|
425
|
+
errors.push(statusResult.error);
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
backlogCleaned: backlogResult.cleaned,
|
|
429
|
+
statusCleaned: statusResult.cleaned,
|
|
430
|
+
cleaned: backlogResult.cleaned || statusResult.cleaned,
|
|
431
|
+
errors,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WU-1747: Retry Strategy Module
|
|
3
|
+
*
|
|
4
|
+
* Provides exponential backoff retry mechanism with configurable parameters
|
|
5
|
+
* for wu:done concurrent load resilience.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Configurable max attempts, base delay, multiplier
|
|
9
|
+
* - Exponential backoff with optional jitter
|
|
10
|
+
* - Presets for common scenarios (wu_done, recovery)
|
|
11
|
+
* - Callback hooks for retry events
|
|
12
|
+
* - Conditional retry based on error type
|
|
13
|
+
*
|
|
14
|
+
* @module retry-strategy
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Error message patterns that are considered retryable for wu:done operations
|
|
18
|
+
* Exported for test consistency
|
|
19
|
+
*/
|
|
20
|
+
export declare const RETRYABLE_ERROR_PATTERNS: Readonly<{
|
|
21
|
+
FAST_FORWARD: "fast-forward";
|
|
22
|
+
NOT_POSSIBLE: "not possible";
|
|
23
|
+
CANNOT_LOCK_REF: "Cannot lock ref";
|
|
24
|
+
FETCH: "fetch";
|
|
25
|
+
PUSH: "push";
|
|
26
|
+
ETIMEDOUT: "ETIMEDOUT";
|
|
27
|
+
ECONNRESET: "ECONNRESET";
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {Object} RetryConfig
|
|
31
|
+
* @property {number} maxAttempts - Maximum number of attempts (default: 5)
|
|
32
|
+
* @property {number} baseDelayMs - Base delay in milliseconds (default: 1000)
|
|
33
|
+
* @property {number} maxDelayMs - Maximum delay cap in milliseconds (default: 30000)
|
|
34
|
+
* @property {number} multiplier - Exponential multiplier (default: 2)
|
|
35
|
+
* @property {number} jitter - Jitter factor 0-1 (default: 0.1 for 10%)
|
|
36
|
+
* @property {function} [shouldRetry] - Optional function(error) => boolean to determine if error is retryable
|
|
37
|
+
* @property {function} [onRetry] - Optional callback(attempt, error, delay) before each retry
|
|
38
|
+
*/
|
|
39
|
+
/**
|
|
40
|
+
* Default retry configuration
|
|
41
|
+
* Balanced for typical wu:done operations
|
|
42
|
+
*/
|
|
43
|
+
export declare const DEFAULT_RETRY_CONFIG: Readonly<{
|
|
44
|
+
maxAttempts: 5;
|
|
45
|
+
baseDelayMs: 1000;
|
|
46
|
+
maxDelayMs: 30000;
|
|
47
|
+
multiplier: 2;
|
|
48
|
+
jitter: 0.1;
|
|
49
|
+
shouldRetry: (_error?: unknown) => true;
|
|
50
|
+
onRetry: any;
|
|
51
|
+
}>;
|
|
52
|
+
/**
|
|
53
|
+
* Pre-configured retry presets for common scenarios
|
|
54
|
+
*/
|
|
55
|
+
export declare const RETRY_PRESETS: Readonly<{
|
|
56
|
+
/**
|
|
57
|
+
* Preset for wu:done merge operations
|
|
58
|
+
* Higher attempts and longer delays for handling concurrent load
|
|
59
|
+
*/
|
|
60
|
+
wu_done: Readonly<{
|
|
61
|
+
maxAttempts: 6;
|
|
62
|
+
baseDelayMs: 2000;
|
|
63
|
+
maxDelayMs: 60000;
|
|
64
|
+
multiplier: 2;
|
|
65
|
+
jitter: 0.15;
|
|
66
|
+
shouldRetry: (error: any) => boolean;
|
|
67
|
+
onRetry: any;
|
|
68
|
+
}>;
|
|
69
|
+
/**
|
|
70
|
+
* Preset for zombie state recovery
|
|
71
|
+
* More attempts with shorter delays
|
|
72
|
+
*/
|
|
73
|
+
recovery: Readonly<{
|
|
74
|
+
maxAttempts: 4;
|
|
75
|
+
baseDelayMs: 500;
|
|
76
|
+
maxDelayMs: 10000;
|
|
77
|
+
multiplier: 2;
|
|
78
|
+
jitter: 0.1;
|
|
79
|
+
shouldRetry: () => true;
|
|
80
|
+
onRetry: any;
|
|
81
|
+
}>;
|
|
82
|
+
/**
|
|
83
|
+
* Preset for quick operations (file I/O, local git)
|
|
84
|
+
* Fast retries for transient errors
|
|
85
|
+
*/
|
|
86
|
+
quick: Readonly<{
|
|
87
|
+
maxAttempts: 3;
|
|
88
|
+
baseDelayMs: 100;
|
|
89
|
+
maxDelayMs: 2000;
|
|
90
|
+
multiplier: 2;
|
|
91
|
+
jitter: 0.05;
|
|
92
|
+
shouldRetry: () => true;
|
|
93
|
+
onRetry: any;
|
|
94
|
+
}>;
|
|
95
|
+
}>;
|
|
96
|
+
/**
|
|
97
|
+
* Create a retry configuration by merging defaults with custom options
|
|
98
|
+
*
|
|
99
|
+
* @param {string|Object} [presetOrOptions] - Preset name or custom options
|
|
100
|
+
* @param {Object} [options] - Additional options to merge (when first arg is preset name)
|
|
101
|
+
* @returns {RetryConfig} Complete retry configuration
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* // Use defaults
|
|
105
|
+
* const config = createRetryConfig();
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* // Customize defaults
|
|
109
|
+
* const config = createRetryConfig({ maxAttempts: 10 });
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* // Use preset
|
|
113
|
+
* const config = createRetryConfig('wu_done');
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* // Customize preset
|
|
117
|
+
* const config = createRetryConfig('wu_done', { maxAttempts: 10 });
|
|
118
|
+
*/
|
|
119
|
+
export declare function createRetryConfig(presetOrOptions: any, options: any): any;
|
|
120
|
+
/**
|
|
121
|
+
* Calculate the backoff delay for a given attempt number
|
|
122
|
+
*
|
|
123
|
+
* Uses exponential backoff formula: baseDelay * (multiplier ^ attempt)
|
|
124
|
+
* with optional jitter to spread concurrent retry attempts
|
|
125
|
+
*
|
|
126
|
+
* @param {number} attempt - Zero-based attempt number (0 = first attempt)
|
|
127
|
+
* @param {RetryConfig} config - Retry configuration
|
|
128
|
+
* @returns {number} Delay in milliseconds
|
|
129
|
+
*/
|
|
130
|
+
export declare function calculateBackoffDelay(attempt: any, config: any): number;
|
|
131
|
+
/**
|
|
132
|
+
* Execute a function with retry logic and exponential backoff
|
|
133
|
+
*
|
|
134
|
+
* @template T
|
|
135
|
+
* @param {function(): Promise<T>} fn - Async function to execute
|
|
136
|
+
* @param {RetryConfig} [config] - Retry configuration (uses defaults if not provided)
|
|
137
|
+
* @returns {Promise<T>} Result of successful execution
|
|
138
|
+
* @throws {Error} Last error if all attempts fail
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* const result = await withRetry(
|
|
142
|
+
* async () => await mergeBranch(),
|
|
143
|
+
* createRetryConfig('wu_done')
|
|
144
|
+
* );
|
|
145
|
+
*/
|
|
146
|
+
export declare function withRetry(fn: any, config?: Readonly<{
|
|
147
|
+
maxAttempts: 5;
|
|
148
|
+
baseDelayMs: 1000;
|
|
149
|
+
maxDelayMs: 30000;
|
|
150
|
+
multiplier: 2;
|
|
151
|
+
jitter: 0.1;
|
|
152
|
+
shouldRetry: (_error?: unknown) => true;
|
|
153
|
+
onRetry: any;
|
|
154
|
+
}>): Promise<any>;
|
|
155
|
+
/**
|
|
156
|
+
* Higher-order function to wrap a function with retry logic
|
|
157
|
+
*
|
|
158
|
+
* @template T
|
|
159
|
+
* @param {function(...args): Promise<T>} fn - Function to wrap
|
|
160
|
+
* @param {RetryConfig} [config] - Retry configuration
|
|
161
|
+
* @returns {function(...args): Promise<T>} Wrapped function with retry logic
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* const retryableMerge = withRetryWrapper(mergeBranch, createRetryConfig('wu_done'));
|
|
165
|
+
* await retryableMerge(branch);
|
|
166
|
+
*/
|
|
167
|
+
export declare function withRetryWrapper(fn: any, config?: Readonly<{
|
|
168
|
+
maxAttempts: 5;
|
|
169
|
+
baseDelayMs: 1000;
|
|
170
|
+
maxDelayMs: 30000;
|
|
171
|
+
multiplier: 2;
|
|
172
|
+
jitter: 0.1;
|
|
173
|
+
shouldRetry: (_error?: unknown) => true;
|
|
174
|
+
onRetry: any;
|
|
175
|
+
}>): (...args: any[]) => Promise<any>;
|
|
176
|
+
/**
|
|
177
|
+
* Determine if an error is a git conflict error (non-retryable)
|
|
178
|
+
*
|
|
179
|
+
* @param {Error} error - Error to check
|
|
180
|
+
* @returns {boolean} True if conflict error
|
|
181
|
+
*/
|
|
182
|
+
export declare function isConflictError(error: any): any;
|
|
183
|
+
/**
|
|
184
|
+
* Determine if an error is a network/transient error (retryable)
|
|
185
|
+
*
|
|
186
|
+
* @param {Error} error - Error to check
|
|
187
|
+
* @returns {boolean} True if likely transient
|
|
188
|
+
*/
|
|
189
|
+
export declare function isTransientError(error: any): any;
|