@xdevops/issue-auto-finish 1.0.91 → 1.0.92
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/dist/{chunk-CQ66LL7P.js → chunk-2WDVTLVF.js} +1 -1
- package/dist/{chunk-UMQYEYLO.js → chunk-6T7ZHAV2.js} +2 -2
- package/dist/{chunk-LDGK5NMS.js → chunk-WZGEYHCC.js} +628 -442
- package/dist/{chunk-LDGK5NMS.js.map → chunk-WZGEYHCC.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.js +2 -2
- package/dist/lib.js +1 -1
- package/dist/lifecycle/DefaultLifecycleHook.d.ts +21 -0
- package/dist/lifecycle/DefaultLifecycleHook.d.ts.map +1 -0
- package/dist/lifecycle/FeedbackTypes.d.ts +52 -0
- package/dist/lifecycle/FeedbackTypes.d.ts.map +1 -0
- package/dist/lifecycle/PhaseLifecycleHook.d.ts +70 -0
- package/dist/lifecycle/PhaseLifecycleHook.d.ts.map +1 -0
- package/dist/lifecycle/PhaseMiddleware.d.ts +47 -0
- package/dist/lifecycle/PhaseMiddleware.d.ts.map +1 -0
- package/dist/lifecycle/PhaseStateMachine.d.ts +111 -0
- package/dist/lifecycle/PhaseStateMachine.d.ts.map +1 -0
- package/dist/lifecycle/index.d.ts +8 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/orchestrator/steps/PhaseHelpers.d.ts +24 -0
- package/dist/orchestrator/steps/PhaseHelpers.d.ts.map +1 -0
- package/dist/orchestrator/steps/PhaseLoopStep.d.ts +10 -0
- package/dist/orchestrator/steps/PhaseLoopStep.d.ts.map +1 -1
- package/dist/orchestrator/strategies/AiPhaseStrategy.d.ts +17 -0
- package/dist/orchestrator/strategies/AiPhaseStrategy.d.ts.map +1 -0
- package/dist/orchestrator/strategies/GateStrategy.d.ts +15 -0
- package/dist/orchestrator/strategies/GateStrategy.d.ts.map +1 -0
- package/dist/orchestrator/strategies/PhaseStrategy.d.ts +16 -0
- package/dist/orchestrator/strategies/PhaseStrategy.d.ts.map +1 -0
- package/dist/orchestrator/strategies/VerifyFixStrategy.d.ts +15 -0
- package/dist/orchestrator/strategies/VerifyFixStrategy.d.ts.map +1 -0
- package/dist/orchestrator/strategies/index.d.ts +17 -0
- package/dist/orchestrator/strategies/index.d.ts.map +1 -0
- package/dist/{restart-MSUWF4ID.js → restart-5D3ZDD5L.js} +2 -2
- package/dist/run.js +2 -2
- package/dist/{start-B4CDZAFG.js → start-IQBNXLEI.js} +2 -2
- package/package.json +1 -1
- package/src/web/frontend/dist/assets/{index-BoYtsxGN.js → index-BR0UoQER.js} +19 -19
- package/src/web/frontend/dist/index.html +1 -1
- /package/dist/{chunk-CQ66LL7P.js.map → chunk-2WDVTLVF.js.map} +0 -0
- /package/dist/{chunk-UMQYEYLO.js.map → chunk-6T7ZHAV2.js.map} +0 -0
- /package/dist/{restart-MSUWF4ID.js.map → restart-5D3ZDD5L.js.map} +0 -0
- /package/dist/{start-B4CDZAFG.js.map → start-IQBNXLEI.js.map} +0 -0
|
@@ -4304,6 +4304,44 @@ async function executeSetup(ctx, deps) {
|
|
|
4304
4304
|
return { wtGit, wtPlan, wtGitMap };
|
|
4305
4305
|
}
|
|
4306
4306
|
|
|
4307
|
+
// src/lifecycle/FeedbackTypes.ts
|
|
4308
|
+
function inputRequestToFeedback(request) {
|
|
4309
|
+
switch (request.type) {
|
|
4310
|
+
case "interactive-dialog":
|
|
4311
|
+
return {
|
|
4312
|
+
kind: "interactive-dialog",
|
|
4313
|
+
question: request.content,
|
|
4314
|
+
options: request.options ?? []
|
|
4315
|
+
};
|
|
4316
|
+
case "plan-approval":
|
|
4317
|
+
return { kind: "approval-required", scope: "plan" };
|
|
4318
|
+
case "generic":
|
|
4319
|
+
return { kind: "generic", content: request.content };
|
|
4320
|
+
}
|
|
4321
|
+
}
|
|
4322
|
+
function feedbackResponseToString(response) {
|
|
4323
|
+
switch (response.action) {
|
|
4324
|
+
case "approve":
|
|
4325
|
+
return "allow";
|
|
4326
|
+
case "reject":
|
|
4327
|
+
return "reject";
|
|
4328
|
+
case "select":
|
|
4329
|
+
return response.value;
|
|
4330
|
+
case "dismiss":
|
|
4331
|
+
return "";
|
|
4332
|
+
case "pause":
|
|
4333
|
+
return "";
|
|
4334
|
+
}
|
|
4335
|
+
}
|
|
4336
|
+
function stringToFeedbackResponse(str, originalFeedback) {
|
|
4337
|
+
if (str === "allow") return { action: "approve" };
|
|
4338
|
+
if (str === "reject") return { action: "reject" };
|
|
4339
|
+
if (str === "") {
|
|
4340
|
+
return originalFeedback.kind === "interactive-dialog" ? { action: "dismiss" } : { action: "approve" };
|
|
4341
|
+
}
|
|
4342
|
+
return { action: "select", value: str };
|
|
4343
|
+
}
|
|
4344
|
+
|
|
4307
4345
|
// src/notesync/NoteSyncSettings.ts
|
|
4308
4346
|
var noteSyncOverride;
|
|
4309
4347
|
function getNoteSyncEnabled(cfg) {
|
|
@@ -4360,8 +4398,14 @@ function clearPendingDialog(issueIid) {
|
|
|
4360
4398
|
store.delete(issueIid);
|
|
4361
4399
|
}
|
|
4362
4400
|
|
|
4363
|
-
// src/orchestrator/steps/
|
|
4364
|
-
var logger17 = logger.child("
|
|
4401
|
+
// src/orchestrator/steps/PhaseHelpers.ts
|
|
4402
|
+
var logger17 = logger.child("PhaseHelpers");
|
|
4403
|
+
async function safeComment(deps, issueId, message) {
|
|
4404
|
+
try {
|
|
4405
|
+
await deps.gongfeng.createIssueNote(issueId, message);
|
|
4406
|
+
} catch {
|
|
4407
|
+
}
|
|
4408
|
+
}
|
|
4365
4409
|
function resolveVerifyRunner(deps) {
|
|
4366
4410
|
return deps.aiRunner;
|
|
4367
4411
|
}
|
|
@@ -4434,18 +4478,6 @@ async function syncResultToIssue(phase, ctx, displayId, phaseName, deps, issueId
|
|
|
4434
4478
|
await safeComment(deps, issueId, issueProgressComment(phaseName, "completed"));
|
|
4435
4479
|
}
|
|
4436
4480
|
}
|
|
4437
|
-
function buildInputHandler(displayId, phaseName, deps) {
|
|
4438
|
-
const useAcpGate = deps.config.ai.codebuddyAcpAutoApprove === false;
|
|
4439
|
-
return (request) => {
|
|
4440
|
-
if (request.type === "interactive-dialog") {
|
|
4441
|
-
return handleInteractiveDialog(displayId, phaseName, deps, request);
|
|
4442
|
-
}
|
|
4443
|
-
if (request.type === "plan-approval" && useAcpGate) {
|
|
4444
|
-
return handlePlanApproval(displayId, phaseName, deps);
|
|
4445
|
-
}
|
|
4446
|
-
return Promise.resolve("allow");
|
|
4447
|
-
};
|
|
4448
|
-
}
|
|
4449
4481
|
function handlePlanApproval(displayId, phaseName, deps) {
|
|
4450
4482
|
logger17.info("ACP plan-approval requested, delegating to review gate", {
|
|
4451
4483
|
issueIid: displayId,
|
|
@@ -4546,39 +4578,69 @@ function updateHooksForPhase(spec, pipelineDef, ctx, wtPlan) {
|
|
|
4546
4578
|
});
|
|
4547
4579
|
}
|
|
4548
4580
|
}
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
}
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
deps.eventBus.emitTyped("agent:output", {
|
|
4567
|
-
issueIid: displayId,
|
|
4568
|
-
phase: spec.name,
|
|
4569
|
-
event: { type: "system", content: t("basePhase.aiStarting", { label: phaseLabel }), timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
4570
|
-
});
|
|
4571
|
-
const callbacks = {
|
|
4572
|
-
onStreamEvent: (event) => deps.eventBus.emitTyped("agent:output", {
|
|
4581
|
+
|
|
4582
|
+
// src/lifecycle/DefaultLifecycleHook.ts
|
|
4583
|
+
var logger18 = logger.child("DefaultLifecycleHook");
|
|
4584
|
+
var DefaultLifecycleHook = class {
|
|
4585
|
+
async beforePhase(ctx) {
|
|
4586
|
+
const { spec, issueCtx, deps, wtPlan } = ctx;
|
|
4587
|
+
const { issue } = issueCtx;
|
|
4588
|
+
const displayId = issue.iid;
|
|
4589
|
+
deps.tracker.updateState(displayId, spec.startState, { currentPhase: spec.name });
|
|
4590
|
+
deps.tracker.updatePhaseProgress(displayId, spec.name, {
|
|
4591
|
+
status: "in_progress",
|
|
4592
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4593
|
+
});
|
|
4594
|
+
wtPlan.updatePhaseProgress(spec.name, "in_progress");
|
|
4595
|
+
await safeComment(deps, issue.id, issueProgressComment(spec.name, "in_progress"));
|
|
4596
|
+
const phaseLabel = t(`phase.${spec.name}`) || spec.name;
|
|
4597
|
+
deps.eventBus.emitTyped("agent:output", {
|
|
4573
4598
|
issueIid: displayId,
|
|
4574
4599
|
phase: spec.name,
|
|
4600
|
+
event: {
|
|
4601
|
+
type: "system",
|
|
4602
|
+
content: t("basePhase.aiStarting", { label: phaseLabel }),
|
|
4603
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4604
|
+
}
|
|
4605
|
+
});
|
|
4606
|
+
}
|
|
4607
|
+
onStream(ctx, event) {
|
|
4608
|
+
const { spec, issueCtx, deps } = ctx;
|
|
4609
|
+
deps.eventBus.emitTyped("agent:output", {
|
|
4610
|
+
issueIid: issueCtx.issue.iid,
|
|
4611
|
+
phase: spec.name,
|
|
4575
4612
|
event
|
|
4576
|
-
})
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4613
|
+
});
|
|
4614
|
+
}
|
|
4615
|
+
beforeExecute;
|
|
4616
|
+
afterExecute;
|
|
4617
|
+
async onFeedback(ctx, feedback) {
|
|
4618
|
+
const { deps, displayId, spec } = ctx;
|
|
4619
|
+
if (feedback.kind === "interactive-dialog") {
|
|
4620
|
+
const result = await handleInteractiveDialog(displayId, spec.name, deps, {
|
|
4621
|
+
type: "interactive-dialog",
|
|
4622
|
+
content: feedback.question,
|
|
4623
|
+
options: feedback.options
|
|
4624
|
+
});
|
|
4625
|
+
return stringToFeedbackResponse(result, feedback);
|
|
4626
|
+
}
|
|
4627
|
+
if (feedback.kind === "approval-required" && feedback.scope === "plan") {
|
|
4628
|
+
const useAcpGate = deps.config.ai.codebuddyAcpAutoApprove === false;
|
|
4629
|
+
if (useAcpGate) {
|
|
4630
|
+
const result = await handlePlanApproval(displayId, spec.name, deps);
|
|
4631
|
+
return result === "allow" ? { action: "approve" } : { action: "reject" };
|
|
4632
|
+
}
|
|
4633
|
+
return { action: "approve" };
|
|
4634
|
+
}
|
|
4635
|
+
return { action: "approve" };
|
|
4636
|
+
}
|
|
4637
|
+
async afterPhase(ctx, outcome) {
|
|
4638
|
+
const { spec, issueCtx, deps, wtGit, wtPlan, wtGitMap, phase } = ctx;
|
|
4639
|
+
const { issue, phaseCtx } = issueCtx;
|
|
4640
|
+
const displayId = issue.iid;
|
|
4641
|
+
if (outcome.sessionId) {
|
|
4642
|
+
wtPlan.updatePhaseSessionId(spec.name, outcome.sessionId);
|
|
4643
|
+
}
|
|
4582
4644
|
deps.tracker.updateState(displayId, spec.doneState, { currentPhase: spec.name });
|
|
4583
4645
|
deps.tracker.updatePhaseProgress(displayId, spec.name, {
|
|
4584
4646
|
status: "completed",
|
|
@@ -4586,28 +4648,400 @@ async function runPhaseWithLifecycle(phase, phaseCtx, spec, ctx, deps, wtGit, wt
|
|
|
4586
4648
|
});
|
|
4587
4649
|
wtPlan.updatePhaseProgress(spec.name, "completed");
|
|
4588
4650
|
await commitPlanFiles(phaseCtx, wtGit, wtGitMap, spec.name, displayId);
|
|
4589
|
-
|
|
4590
|
-
|
|
4651
|
+
if (phase) {
|
|
4652
|
+
await syncResultToIssue(phase, phaseCtx, displayId, spec.name, deps, issue.id, wtPlan);
|
|
4653
|
+
}
|
|
4654
|
+
}
|
|
4655
|
+
async onError(ctx, error) {
|
|
4656
|
+
const { spec, issueCtx, deps, wtPlan } = ctx;
|
|
4657
|
+
const { issue } = issueCtx;
|
|
4658
|
+
const displayId = issue.iid;
|
|
4659
|
+
wtPlan.updatePhaseProgress(spec.name, "failed", error.message);
|
|
4660
|
+
deps.tracker.updatePhaseProgress(displayId, spec.name, { status: "failed" });
|
|
4661
|
+
if (error.wasActiveAtTimeout) {
|
|
4662
|
+
deps.tracker.markFailedSoft(displayId, error.message, "phase_running" /* PhaseRunning */);
|
|
4663
|
+
} else {
|
|
4664
|
+
deps.tracker.markFailed(displayId, error.message, "phase_running" /* PhaseRunning */);
|
|
4665
|
+
}
|
|
4666
|
+
const shortErr = error.message.slice(0, 200);
|
|
4667
|
+
await safeComment(deps, issue.id, issueProgressComment(spec.name, "failed", shortErr));
|
|
4668
|
+
return { action: "fail", softFail: error.wasActiveAtTimeout };
|
|
4591
4669
|
}
|
|
4592
|
-
|
|
4593
|
-
|
|
4670
|
+
};
|
|
4671
|
+
function createCallbacksFromHook(hook, ctx) {
|
|
4672
|
+
return {
|
|
4673
|
+
onStreamEvent: hook.onStream ? (event) => hook.onStream(ctx, event) : void 0,
|
|
4674
|
+
onInputRequired: hook.onFeedback ? async (request) => {
|
|
4675
|
+
const feedback = inputRequestToFeedback(request);
|
|
4676
|
+
const response = await hook.onFeedback(ctx, feedback);
|
|
4677
|
+
return feedbackResponseToString(response);
|
|
4678
|
+
} : void 0
|
|
4679
|
+
};
|
|
4680
|
+
}
|
|
4681
|
+
|
|
4682
|
+
// src/orchestrator/strategies/GateStrategy.ts
|
|
4683
|
+
var logger19 = logger.child("GateStrategy");
|
|
4684
|
+
var GateStrategy = class {
|
|
4685
|
+
name = "gate";
|
|
4686
|
+
shouldSkip() {
|
|
4687
|
+
return false;
|
|
4594
4688
|
}
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4689
|
+
async execute(ctx, _hooks) {
|
|
4690
|
+
const { spec, issueCtx, deps, wtPlan } = ctx;
|
|
4691
|
+
const { issue } = issueCtx;
|
|
4692
|
+
if (deps.shouldAutoApprove(issue.labels)) {
|
|
4693
|
+
logger19.info("Auto-approving review gate (matched autoApproveLabels)", {
|
|
4694
|
+
iid: issue.iid,
|
|
4695
|
+
labels: issue.labels,
|
|
4696
|
+
autoApproveLabels: deps.config.review.autoApproveLabels
|
|
4697
|
+
});
|
|
4698
|
+
if (spec.approvedState) {
|
|
4699
|
+
deps.tracker.updateState(issue.iid, spec.approvedState, { currentPhase: spec.name });
|
|
4700
|
+
}
|
|
4701
|
+
wtPlan.updatePhaseProgress(spec.name, "completed");
|
|
4702
|
+
deps.tracker.updatePhaseProgress(issue.iid, spec.name, {
|
|
4703
|
+
status: "completed",
|
|
4704
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4705
|
+
});
|
|
4706
|
+
try {
|
|
4707
|
+
await deps.gongfeng.createIssueNote(issue.id, t("orchestrator.autoApproveComment"));
|
|
4708
|
+
} catch {
|
|
4709
|
+
}
|
|
4710
|
+
return { paused: false };
|
|
4711
|
+
}
|
|
4712
|
+
deps.tracker.updateState(issue.iid, spec.startState, { currentPhase: spec.name });
|
|
4713
|
+
wtPlan.updatePhaseProgress(spec.name, "in_progress");
|
|
4714
|
+
deps.tracker.updatePhaseProgress(issue.iid, spec.name, {
|
|
4715
|
+
status: "in_progress",
|
|
4716
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4717
|
+
});
|
|
4718
|
+
deps.eventBus.emitTyped("review:requested", { issueIid: issue.iid });
|
|
4719
|
+
logger19.info("Review gate reached, pausing", { iid: issue.iid });
|
|
4720
|
+
return { paused: true };
|
|
4721
|
+
}
|
|
4722
|
+
};
|
|
4723
|
+
|
|
4724
|
+
// src/orchestrator/strategies/AiPhaseStrategy.ts
|
|
4725
|
+
var logger20 = logger.child("AiPhaseStrategy");
|
|
4726
|
+
var AiPhaseStrategy = class {
|
|
4727
|
+
name = "ai";
|
|
4728
|
+
shouldSkip(ctx) {
|
|
4729
|
+
const { spec, issueCtx, deps } = ctx;
|
|
4730
|
+
if (spec.name === "uat" && !isE2eEnabledForIssue(issueCtx.issue.iid, deps.tracker, deps.config)) {
|
|
4731
|
+
logger20.info("UAT phase skipped (E2E not enabled for this issue)", { iid: issueCtx.issue.iid });
|
|
4732
|
+
return true;
|
|
4733
|
+
}
|
|
4734
|
+
return false;
|
|
4735
|
+
}
|
|
4736
|
+
async execute(ctx, hooks) {
|
|
4737
|
+
const { spec, issueCtx, deps, wtGit, wtPlan, wtGitMap } = ctx;
|
|
4738
|
+
const { issue, phaseCtx } = issueCtx;
|
|
4739
|
+
if (this.shouldSkip(ctx)) {
|
|
4740
|
+
deps.tracker.updateState(issue.iid, spec.doneState, { currentPhase: spec.name });
|
|
4741
|
+
wtPlan.updatePhaseProgress(spec.name, "completed");
|
|
4742
|
+
deps.tracker.updatePhaseProgress(issue.iid, spec.name, {
|
|
4743
|
+
status: "completed",
|
|
4744
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4745
|
+
});
|
|
4746
|
+
return { paused: false };
|
|
4747
|
+
}
|
|
4748
|
+
updateHooksForPhase(spec, issueCtx.pipelineDef, issueCtx, wtPlan);
|
|
4749
|
+
const runner = this.resolveRunner(ctx);
|
|
4750
|
+
if (spec.name === "uat") {
|
|
4751
|
+
const runnerName = runner === deps.e2eAiRunner ? "e2eAiRunner (CodeBuddy)" : "mainRunner";
|
|
4752
|
+
logger20.info("UAT phase starting", { iid: issue.iid, runner: runnerName });
|
|
4753
|
+
}
|
|
4754
|
+
const phase = createPhase(spec.name, runner, wtGit, wtPlan, deps.config);
|
|
4755
|
+
if (wtGitMap) phase.setWtGitMap(wtGitMap);
|
|
4756
|
+
ctx.phase = phase;
|
|
4757
|
+
const outcome = await runPhaseWithLifecycle(
|
|
4758
|
+
phase,
|
|
4759
|
+
phaseCtx,
|
|
4760
|
+
spec,
|
|
4761
|
+
issueCtx,
|
|
4762
|
+
deps,
|
|
4763
|
+
wtGit,
|
|
4764
|
+
wtPlan,
|
|
4765
|
+
wtGitMap,
|
|
4766
|
+
hooks
|
|
4767
|
+
);
|
|
4768
|
+
if (outcome.status === "running") {
|
|
4769
|
+
return this.handleAsyncOutcome(ctx, outcome);
|
|
4770
|
+
}
|
|
4771
|
+
if (spec.approvedState && outcome.data?.hasReleaseCapability) {
|
|
4772
|
+
return this.handleGateRequest(ctx);
|
|
4773
|
+
}
|
|
4774
|
+
return { paused: false };
|
|
4775
|
+
}
|
|
4776
|
+
resolveRunner(ctx) {
|
|
4777
|
+
const { spec, deps, displayId } = ctx;
|
|
4778
|
+
if (spec.name === "verify") return resolveVerifyRunner(deps);
|
|
4779
|
+
if (spec.name === "uat") return resolveUatRunner(deps, displayId);
|
|
4780
|
+
return deps.aiRunner;
|
|
4781
|
+
}
|
|
4782
|
+
async handleAsyncOutcome(ctx, outcome) {
|
|
4783
|
+
const { spec, issueCtx, deps, wtGit, wtPlan, wtGitMap } = ctx;
|
|
4784
|
+
const { issue, phaseCtx } = issueCtx;
|
|
4785
|
+
if (outcome.awaitCompletion) {
|
|
4786
|
+
logger20.info("Async phase running, awaiting completion", { iid: issue.iid, phase: spec.name });
|
|
4787
|
+
const finalOutcome = await outcome.awaitCompletion;
|
|
4788
|
+
if (finalOutcome.sessionId) {
|
|
4789
|
+
wtPlan.updatePhaseSessionId(spec.name, finalOutcome.sessionId);
|
|
4790
|
+
}
|
|
4791
|
+
if (finalOutcome.status === "completed") {
|
|
4792
|
+
deps.tracker.updateState(issue.iid, spec.doneState, { currentPhase: spec.name });
|
|
4793
|
+
deps.tracker.updatePhaseProgress(issue.iid, spec.name, {
|
|
4794
|
+
status: "completed",
|
|
4795
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4796
|
+
});
|
|
4797
|
+
wtPlan.updatePhaseProgress(spec.name, "completed");
|
|
4798
|
+
await commitPlanFiles(phaseCtx, wtGit, wtGitMap, spec.name, issue.iid);
|
|
4799
|
+
const runner = this.resolveRunner(ctx);
|
|
4800
|
+
const phase = createPhase(spec.name, runner, wtGit, wtPlan, deps.config);
|
|
4801
|
+
await syncResultToIssue(phase, phaseCtx, issue.iid, spec.name, deps, issue.id, wtPlan);
|
|
4802
|
+
logger20.info("Async phase completed successfully", { iid: issue.iid, phase: spec.name });
|
|
4803
|
+
return { paused: false };
|
|
4804
|
+
}
|
|
4805
|
+
const errMsg = finalOutcome.error?.message ?? "Unknown error";
|
|
4806
|
+
const shortErr = errMsg.slice(0, 200);
|
|
4807
|
+
const wasActive = finalOutcome.error?.wasActiveAtTimeout ?? false;
|
|
4808
|
+
wtPlan.updatePhaseProgress(spec.name, "failed", errMsg);
|
|
4809
|
+
deps.tracker.updatePhaseProgress(issue.iid, spec.name, { status: "failed" });
|
|
4810
|
+
if (wasActive) {
|
|
4811
|
+
deps.tracker.markFailedSoft(issue.iid, errMsg, "phase_running" /* PhaseRunning */);
|
|
4812
|
+
} else {
|
|
4813
|
+
deps.tracker.markFailed(issue.iid, errMsg, "phase_running" /* PhaseRunning */);
|
|
4814
|
+
}
|
|
4815
|
+
await safeComment(deps, issue.id, issueProgressComment(spec.name, "failed", shortErr));
|
|
4816
|
+
throw new AIExecutionError(spec.name, `Phase ${spec.name} failed: ${shortErr}`, {
|
|
4817
|
+
output: finalOutcome.error?.rawOutput ?? finalOutcome.output,
|
|
4818
|
+
exitCode: finalOutcome.exitCode ?? 1,
|
|
4819
|
+
isRetryable: finalOutcome.error?.isRetryable,
|
|
4820
|
+
wasActiveAtTimeout: wasActive
|
|
4821
|
+
});
|
|
4822
|
+
}
|
|
4823
|
+
deps.tracker.updateState(issue.iid, "phase_waiting" /* PhaseWaiting */, { currentPhase: spec.name });
|
|
4824
|
+
wtPlan.updatePhaseProgress(spec.name, "gate_waiting");
|
|
4825
|
+
deps.tracker.updatePhaseProgress(issue.iid, spec.name, { status: "gate_waiting" });
|
|
4826
|
+
const gateEvent = spec.name === "uat" ? "uat:gateRequested" : "release:gateRequested";
|
|
4827
|
+
deps.eventBus.emitTyped(gateEvent, { issueIid: issue.iid });
|
|
4828
|
+
logger20.info("Async phase running (no awaitCompletion), pausing pipeline", {
|
|
4829
|
+
iid: issue.iid,
|
|
4830
|
+
phase: spec.name
|
|
4831
|
+
});
|
|
4832
|
+
return { paused: true };
|
|
4833
|
+
}
|
|
4834
|
+
handleGateRequest(ctx) {
|
|
4835
|
+
const { spec, issueCtx, deps, wtPlan } = ctx;
|
|
4836
|
+
const { issue } = issueCtx;
|
|
4837
|
+
deps.tracker.updateState(issue.iid, "phase_waiting" /* PhaseWaiting */, { currentPhase: spec.name });
|
|
4838
|
+
wtPlan.updatePhaseProgress(spec.name, "gate_waiting");
|
|
4839
|
+
deps.tracker.updatePhaseProgress(issue.iid, spec.name, { status: "gate_waiting" });
|
|
4840
|
+
deps.eventBus.emitTyped("release:gateRequested", { issueIid: issue.iid });
|
|
4841
|
+
logger20.info("Phase requested gate, pausing", { iid: issue.iid, phase: spec.name });
|
|
4842
|
+
return { paused: true };
|
|
4843
|
+
}
|
|
4844
|
+
};
|
|
4845
|
+
|
|
4846
|
+
// src/orchestrator/strategies/VerifyFixStrategy.ts
|
|
4847
|
+
var logger21 = logger.child("VerifyFixStrategy");
|
|
4848
|
+
var VerifyFixStrategy = class {
|
|
4849
|
+
name = "verify-fix";
|
|
4850
|
+
shouldSkip() {
|
|
4851
|
+
return false;
|
|
4852
|
+
}
|
|
4853
|
+
async execute(ctx, hooks) {
|
|
4854
|
+
const { issueCtx, deps, wtGit, wtPlan, wtGitMap } = ctx;
|
|
4855
|
+
const { issue, phaseCtx, pipelineDef } = issueCtx;
|
|
4856
|
+
const maxIterations = deps.config.verifyFixLoop.maxIterations;
|
|
4857
|
+
const verifySpec = ctx.spec;
|
|
4858
|
+
const verifyPhaseIdx = pipelineDef.phases.findIndex((p) => p.name === verifySpec.name);
|
|
4859
|
+
const buildPhaseIdx = this.findPreviousAiPhaseIndex(pipelineDef.phases, verifyPhaseIdx);
|
|
4860
|
+
deps.eventBus.emitTyped("verify:loopStarted", {
|
|
4861
|
+
issueIid: issue.iid,
|
|
4862
|
+
maxIterations
|
|
4863
|
+
});
|
|
4864
|
+
logger21.info("Verify-fix loop started", {
|
|
4865
|
+
iid: issue.iid,
|
|
4866
|
+
maxIterations,
|
|
4867
|
+
buildPhaseIdx
|
|
4868
|
+
});
|
|
4869
|
+
for (let iteration = 1; iteration <= maxIterations; iteration++) {
|
|
4870
|
+
if (isShuttingDown()) throw new ServiceShutdownError();
|
|
4871
|
+
logger21.info("Verify-fix loop iteration", { iteration, maxIterations, iid: issue.iid });
|
|
4872
|
+
updateHooksForPhase(verifySpec, pipelineDef, issueCtx, wtPlan);
|
|
4873
|
+
const verifyRunner = resolveVerifyRunner(deps);
|
|
4874
|
+
const verifyPhase = createPhase("verify", verifyRunner, wtGit, wtPlan, deps.config);
|
|
4875
|
+
if (wtGitMap) verifyPhase.setWtGitMap(wtGitMap);
|
|
4876
|
+
let verifyOutcome;
|
|
4877
|
+
try {
|
|
4878
|
+
verifyOutcome = await runPhaseWithLifecycle(
|
|
4879
|
+
verifyPhase,
|
|
4880
|
+
phaseCtx,
|
|
4881
|
+
verifySpec,
|
|
4882
|
+
issueCtx,
|
|
4883
|
+
deps,
|
|
4884
|
+
wtGit,
|
|
4885
|
+
wtPlan,
|
|
4886
|
+
wtGitMap,
|
|
4887
|
+
hooks
|
|
4888
|
+
);
|
|
4889
|
+
} catch (err) {
|
|
4890
|
+
logger21.warn("Verify phase execution failed", {
|
|
4891
|
+
iteration,
|
|
4892
|
+
iid: issue.iid,
|
|
4893
|
+
error: err.message
|
|
4894
|
+
});
|
|
4895
|
+
deps.eventBus.emitTyped("verify:iterationComplete", {
|
|
4896
|
+
issueIid: issue.iid,
|
|
4897
|
+
iteration,
|
|
4898
|
+
passed: false,
|
|
4899
|
+
failures: ["AI runner execution failed"]
|
|
4900
|
+
});
|
|
4901
|
+
if (iteration === maxIterations) throw err;
|
|
4902
|
+
if (buildPhaseIdx >= 0) {
|
|
4903
|
+
await this.executeBuildFix(issueCtx, deps, wtGit, wtPlan, buildPhaseIdx, {
|
|
4904
|
+
iteration,
|
|
4905
|
+
verifyFailures: ["AI runner execution failed: " + err.message],
|
|
4906
|
+
rawReport: ""
|
|
4907
|
+
}, wtGitMap, hooks);
|
|
4908
|
+
}
|
|
4909
|
+
continue;
|
|
4910
|
+
}
|
|
4911
|
+
const report = verifyOutcome.data?.verifyReport;
|
|
4912
|
+
const passed = report ? report.passed : true;
|
|
4913
|
+
deps.eventBus.emitTyped("verify:iterationComplete", {
|
|
4914
|
+
issueIid: issue.iid,
|
|
4915
|
+
iteration,
|
|
4916
|
+
passed,
|
|
4917
|
+
failures: report?.failureReasons
|
|
4918
|
+
});
|
|
4919
|
+
if (passed) {
|
|
4920
|
+
logger21.info("Verify-fix loop passed", { iteration, iid: issue.iid });
|
|
4921
|
+
return { paused: false };
|
|
4922
|
+
}
|
|
4923
|
+
logger21.info("Verify failed, issues found", {
|
|
4924
|
+
iteration,
|
|
4925
|
+
iid: issue.iid,
|
|
4926
|
+
failures: report?.failureReasons,
|
|
4927
|
+
todolistStats: report?.todolistStats
|
|
4928
|
+
});
|
|
4929
|
+
if (iteration === maxIterations) {
|
|
4930
|
+
deps.eventBus.emitTyped("verify:loopExhausted", {
|
|
4931
|
+
issueIid: issue.iid,
|
|
4932
|
+
totalIterations: iteration,
|
|
4933
|
+
failures: report?.failureReasons ?? []
|
|
4934
|
+
});
|
|
4935
|
+
const failMsg = `Verify-fix loop exhausted after ${maxIterations} iterations. Remaining issues: ${report?.failureReasons?.join("; ") ?? "unknown"}`;
|
|
4936
|
+
logger21.warn(failMsg, { iid: issue.iid });
|
|
4937
|
+
throw new AIExecutionError("verify", failMsg, {
|
|
4938
|
+
output: report?.rawReport ?? "",
|
|
4939
|
+
exitCode: 0
|
|
4940
|
+
});
|
|
4941
|
+
}
|
|
4942
|
+
if (buildPhaseIdx >= 0) {
|
|
4943
|
+
await this.executeBuildFix(issueCtx, deps, wtGit, wtPlan, buildPhaseIdx, {
|
|
4944
|
+
iteration,
|
|
4945
|
+
verifyFailures: report?.failureReasons ?? [],
|
|
4946
|
+
rawReport: report?.rawReport ?? ""
|
|
4947
|
+
}, wtGitMap, hooks);
|
|
4948
|
+
}
|
|
4949
|
+
}
|
|
4950
|
+
return { paused: false };
|
|
4951
|
+
}
|
|
4952
|
+
async executeBuildFix(ctx, deps, wtGit, wtPlan, buildPhaseIdx, fixContext, wtGitMap, hooks) {
|
|
4953
|
+
const { issue, phaseCtx, pipelineDef } = ctx;
|
|
4954
|
+
const buildSpec = pipelineDef.phases[buildPhaseIdx];
|
|
4955
|
+
logger21.info("Looping back to build for fix", {
|
|
4956
|
+
iteration: fixContext.iteration,
|
|
4957
|
+
iid: issue.iid,
|
|
4958
|
+
failures: fixContext.verifyFailures
|
|
4959
|
+
});
|
|
4960
|
+
phaseCtx.fixContext = fixContext;
|
|
4961
|
+
try {
|
|
4962
|
+
updateHooksForPhase(buildSpec, pipelineDef, ctx, wtPlan);
|
|
4963
|
+
const buildPhase = createPhase("build", deps.aiRunner, wtGit, wtPlan, deps.config);
|
|
4964
|
+
if (wtGitMap) buildPhase.setWtGitMap(wtGitMap);
|
|
4965
|
+
await runPhaseWithLifecycle(
|
|
4966
|
+
buildPhase,
|
|
4967
|
+
phaseCtx,
|
|
4968
|
+
buildSpec,
|
|
4969
|
+
ctx,
|
|
4970
|
+
deps,
|
|
4971
|
+
wtGit,
|
|
4972
|
+
wtPlan,
|
|
4973
|
+
wtGitMap,
|
|
4974
|
+
hooks
|
|
4975
|
+
);
|
|
4976
|
+
} finally {
|
|
4977
|
+
delete phaseCtx.fixContext;
|
|
4978
|
+
}
|
|
4604
4979
|
}
|
|
4605
|
-
|
|
4980
|
+
findPreviousAiPhaseIndex(phases, currentIdx) {
|
|
4981
|
+
for (let j = currentIdx - 1; j >= 0; j--) {
|
|
4982
|
+
if (phases[j].kind === "ai") return j;
|
|
4983
|
+
}
|
|
4984
|
+
return -1;
|
|
4985
|
+
}
|
|
4986
|
+
};
|
|
4987
|
+
|
|
4988
|
+
// src/orchestrator/strategies/index.ts
|
|
4989
|
+
var gateStrategy = new GateStrategy();
|
|
4990
|
+
var aiStrategy = new AiPhaseStrategy();
|
|
4991
|
+
var verifyFixStrategy = new VerifyFixStrategy();
|
|
4992
|
+
function resolveStrategy(spec, config) {
|
|
4993
|
+
if (spec.kind === "gate") {
|
|
4994
|
+
return gateStrategy;
|
|
4995
|
+
}
|
|
4996
|
+
if (spec.name === "verify" && config.verifyFixLoop.enabled) {
|
|
4997
|
+
return verifyFixStrategy;
|
|
4998
|
+
}
|
|
4999
|
+
return aiStrategy;
|
|
5000
|
+
}
|
|
5001
|
+
|
|
5002
|
+
// src/orchestrator/steps/PhaseLoopStep.ts
|
|
5003
|
+
var logger22 = logger.child("PhaseLoopStep");
|
|
5004
|
+
async function runPhaseWithLifecycle(phase, phaseCtx, spec, ctx, deps, wtGit, wtPlan, wtGitMap, hook) {
|
|
5005
|
+
const lifecycleHook = hook ?? new DefaultLifecycleHook();
|
|
5006
|
+
const execCtx = {
|
|
5007
|
+
spec,
|
|
5008
|
+
issueCtx: ctx,
|
|
5009
|
+
deps,
|
|
5010
|
+
wtGit,
|
|
5011
|
+
wtPlan,
|
|
5012
|
+
wtGitMap,
|
|
5013
|
+
phase,
|
|
5014
|
+
displayId: ctx.issue.iid
|
|
5015
|
+
};
|
|
5016
|
+
await lifecycleHook.beforePhase(execCtx);
|
|
5017
|
+
if (lifecycleHook.beforeExecute) {
|
|
5018
|
+
await lifecycleHook.beforeExecute(execCtx);
|
|
5019
|
+
}
|
|
5020
|
+
const callbacks = createCallbacksFromHook(lifecycleHook, execCtx);
|
|
5021
|
+
const outcome = await phase.run(phaseCtx, callbacks);
|
|
5022
|
+
if (outcome.sessionId) wtPlan.updatePhaseSessionId(spec.name, outcome.sessionId);
|
|
5023
|
+
const finalOutcome = lifecycleHook.afterExecute ? await lifecycleHook.afterExecute(execCtx, outcome) : outcome;
|
|
5024
|
+
if (finalOutcome.status === "completed") {
|
|
5025
|
+
await lifecycleHook.afterPhase(execCtx, finalOutcome);
|
|
5026
|
+
return finalOutcome;
|
|
5027
|
+
}
|
|
5028
|
+
if (finalOutcome.status === "running") {
|
|
5029
|
+
return finalOutcome;
|
|
5030
|
+
}
|
|
5031
|
+
const errMsg = finalOutcome.error?.message ?? "Unknown error";
|
|
5032
|
+
const phaseError = {
|
|
5033
|
+
message: errMsg,
|
|
5034
|
+
isRetryable: finalOutcome.error?.isRetryable ?? true,
|
|
5035
|
+
rawOutput: finalOutcome.error?.rawOutput ?? finalOutcome.output,
|
|
5036
|
+
wasActiveAtTimeout: finalOutcome.error?.wasActiveAtTimeout ?? false
|
|
5037
|
+
};
|
|
5038
|
+
await lifecycleHook.onError(execCtx, phaseError);
|
|
5039
|
+
const shortErr = errMsg.slice(0, 200);
|
|
4606
5040
|
throw new AIExecutionError(spec.name, `Phase ${spec.name} failed: ${shortErr}`, {
|
|
4607
|
-
output:
|
|
4608
|
-
exitCode:
|
|
4609
|
-
isRetryable:
|
|
4610
|
-
wasActiveAtTimeout:
|
|
5041
|
+
output: finalOutcome.error?.rawOutput ?? finalOutcome.output,
|
|
5042
|
+
exitCode: finalOutcome.exitCode ?? 1,
|
|
5043
|
+
isRetryable: finalOutcome.error?.isRetryable,
|
|
5044
|
+
wasActiveAtTimeout: finalOutcome.error?.wasActiveAtTimeout ?? false
|
|
4611
5045
|
});
|
|
4612
5046
|
}
|
|
4613
5047
|
async function executePhaseLoop(ctx, deps, wtGit, wtPlan, wtGitMap) {
|
|
@@ -4631,15 +5065,15 @@ async function executePhaseLoop(ctx, deps, wtGit, wtPlan, wtGitMap) {
|
|
|
4631
5065
|
if (skippedDeployPhase && !phaseCtx.ports) {
|
|
4632
5066
|
const existingPorts = deps.getPortsForIssue(issue.iid);
|
|
4633
5067
|
if (existingPorts && deps.isPreviewRunning(issue.iid)) {
|
|
4634
|
-
|
|
5068
|
+
logger22.info("Restored preview ports from allocator", { iid: issue.iid, ...existingPorts });
|
|
4635
5069
|
phaseCtx.ports = existingPorts;
|
|
4636
5070
|
ctx.wtCtx.ports = existingPorts;
|
|
4637
5071
|
serversStarted = true;
|
|
4638
5072
|
} else {
|
|
4639
5073
|
if (existingPorts) {
|
|
4640
|
-
|
|
5074
|
+
logger22.info("Ports allocated but servers not running, restarting", { iid: issue.iid });
|
|
4641
5075
|
} else {
|
|
4642
|
-
|
|
5076
|
+
logger22.info("Restarting preview servers for resumed pipeline", { iid: issue.iid });
|
|
4643
5077
|
}
|
|
4644
5078
|
const ports = await deps.startPreviewServers(ctx.wtCtx, issue);
|
|
4645
5079
|
if (ports) {
|
|
@@ -4651,93 +5085,25 @@ async function executePhaseLoop(ctx, deps, wtGit, wtPlan, wtGitMap) {
|
|
|
4651
5085
|
}
|
|
4652
5086
|
}
|
|
4653
5087
|
if (startIdx > 0) {
|
|
4654
|
-
|
|
4655
|
-
if (currentProgress) {
|
|
4656
|
-
let patched = false;
|
|
4657
|
-
for (let i = 0; i < startIdx; i++) {
|
|
4658
|
-
const prevSpec = pipelineDef.phases[i];
|
|
4659
|
-
const pp = currentProgress.phases[prevSpec.name];
|
|
4660
|
-
if (pp && pp.status !== "completed") {
|
|
4661
|
-
logger17.warn("Fixing stale phase progress", {
|
|
4662
|
-
iid: issue.iid,
|
|
4663
|
-
phase: prevSpec.name,
|
|
4664
|
-
was: pp.status,
|
|
4665
|
-
now: "completed"
|
|
4666
|
-
});
|
|
4667
|
-
pp.status = "completed";
|
|
4668
|
-
if (!pp.completedAt) {
|
|
4669
|
-
pp.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4670
|
-
}
|
|
4671
|
-
patched = true;
|
|
4672
|
-
}
|
|
4673
|
-
}
|
|
4674
|
-
if (patched) {
|
|
4675
|
-
wtPlan.writeProgress(currentProgress);
|
|
4676
|
-
}
|
|
4677
|
-
}
|
|
4678
|
-
if (record.phaseProgress) {
|
|
4679
|
-
for (let i = 0; i < startIdx; i++) {
|
|
4680
|
-
const prevSpec = pipelineDef.phases[i];
|
|
4681
|
-
const tp = record.phaseProgress[prevSpec.name];
|
|
4682
|
-
if (tp && tp.status !== "completed") {
|
|
4683
|
-
deps.tracker.updatePhaseProgress(issue.iid, prevSpec.name, {
|
|
4684
|
-
status: "completed",
|
|
4685
|
-
completedAt: tp.completedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
4686
|
-
});
|
|
4687
|
-
}
|
|
4688
|
-
}
|
|
4689
|
-
}
|
|
5088
|
+
healStaleProgress(ctx, deps, wtPlan, startIdx);
|
|
4690
5089
|
}
|
|
5090
|
+
const hooks = new DefaultLifecycleHook();
|
|
4691
5091
|
for (let i = startIdx; i < pipelineDef.phases.length; i++) {
|
|
4692
|
-
if (isShuttingDown())
|
|
4693
|
-
throw new ServiceShutdownError();
|
|
4694
|
-
}
|
|
5092
|
+
if (isShuttingDown()) throw new ServiceShutdownError();
|
|
4695
5093
|
const spec = pipelineDef.phases[i];
|
|
4696
5094
|
const pendingAction = deps.consumePendingAction?.(issue.iid);
|
|
4697
|
-
if (pendingAction)
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
}
|
|
4710
|
-
wtPlan.updatePhaseProgress(spec.name, "completed");
|
|
4711
|
-
deps.tracker.updatePhaseProgress(issue.iid, spec.name, {
|
|
4712
|
-
status: "completed",
|
|
4713
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4714
|
-
});
|
|
4715
|
-
try {
|
|
4716
|
-
await deps.gongfeng.createIssueNote(
|
|
4717
|
-
issue.id,
|
|
4718
|
-
t("orchestrator.autoApproveComment")
|
|
4719
|
-
);
|
|
4720
|
-
} catch {
|
|
4721
|
-
}
|
|
4722
|
-
continue;
|
|
4723
|
-
}
|
|
4724
|
-
deps.tracker.updateState(issue.iid, spec.startState, { currentPhase: spec.name });
|
|
4725
|
-
wtPlan.updatePhaseProgress(spec.name, "in_progress");
|
|
4726
|
-
deps.tracker.updatePhaseProgress(issue.iid, spec.name, {
|
|
4727
|
-
status: "in_progress",
|
|
4728
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4729
|
-
});
|
|
4730
|
-
deps.eventBus.emitTyped("review:requested", { issueIid: issue.iid });
|
|
4731
|
-
logger17.info("Review gate reached, pausing", { iid: issue.iid });
|
|
4732
|
-
return { serversStarted, paused: true };
|
|
4733
|
-
}
|
|
4734
|
-
if (spec.name === "verify" && deps.config.verifyFixLoop.enabled) {
|
|
4735
|
-
const buildIdx = findPreviousAiPhaseIndex(pipelineDef.phases, i);
|
|
4736
|
-
await executeVerifyFixLoop(ctx, deps, wtGit, wtPlan, i, buildIdx, wtGitMap);
|
|
4737
|
-
continue;
|
|
4738
|
-
}
|
|
4739
|
-
if (spec.name === "uat" && !isE2eEnabledForIssue(issue.iid, deps.tracker, deps.config)) {
|
|
4740
|
-
logger17.info("UAT phase skipped (E2E not enabled for this issue)", { iid: issue.iid });
|
|
5095
|
+
if (pendingAction) throw new PhaseAbortedError(spec.name, pendingAction);
|
|
5096
|
+
const strategy = resolveStrategy(spec, deps.config);
|
|
5097
|
+
const execCtx = {
|
|
5098
|
+
spec,
|
|
5099
|
+
issueCtx: ctx,
|
|
5100
|
+
deps,
|
|
5101
|
+
wtGit,
|
|
5102
|
+
wtPlan,
|
|
5103
|
+
wtGitMap,
|
|
5104
|
+
displayId: issue.iid
|
|
5105
|
+
};
|
|
5106
|
+
if (strategy.shouldSkip(execCtx)) {
|
|
4741
5107
|
deps.tracker.updateState(issue.iid, spec.doneState, { currentPhase: spec.name });
|
|
4742
5108
|
wtPlan.updatePhaseProgress(spec.name, "completed");
|
|
4743
5109
|
deps.tracker.updatePhaseProgress(issue.iid, spec.name, {
|
|
@@ -4746,49 +5112,12 @@ async function executePhaseLoop(ctx, deps, wtGit, wtPlan, wtGitMap) {
|
|
|
4746
5112
|
});
|
|
4747
5113
|
continue;
|
|
4748
5114
|
}
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
const runnerName = runner === deps.e2eAiRunner ? "e2eAiRunner (CodeBuddy)" : "mainRunner";
|
|
4753
|
-
logger17.info("UAT phase starting", { iid: issue.iid, runner: runnerName });
|
|
4754
|
-
}
|
|
4755
|
-
const phase = createPhase(spec.name, runner, wtGit, wtPlan, deps.config);
|
|
4756
|
-
if (wtGitMap) {
|
|
4757
|
-
phase.setWtGitMap(wtGitMap);
|
|
4758
|
-
}
|
|
4759
|
-
const outcome = await runPhaseWithLifecycle(
|
|
4760
|
-
phase,
|
|
4761
|
-
phaseCtx,
|
|
4762
|
-
spec,
|
|
4763
|
-
ctx,
|
|
4764
|
-
deps,
|
|
4765
|
-
wtGit,
|
|
4766
|
-
wtPlan,
|
|
4767
|
-
wtGitMap
|
|
4768
|
-
);
|
|
4769
|
-
if (outcome.status === "running") {
|
|
4770
|
-
if (outcome.awaitCompletion) {
|
|
4771
|
-
logger17.info("Async phase running, awaiting completion", { iid: issue.iid, phase: spec.name });
|
|
4772
|
-
const finalOutcome = await awaitAsyncPhase(outcome, spec, ctx, deps, wtGit, wtPlan, wtGitMap);
|
|
4773
|
-
if (finalOutcome.status === "completed") {
|
|
4774
|
-
continue;
|
|
4775
|
-
}
|
|
4776
|
-
}
|
|
4777
|
-
deps.tracker.updateState(issue.iid, "phase_waiting" /* PhaseWaiting */, { currentPhase: spec.name });
|
|
4778
|
-
wtPlan.updatePhaseProgress(spec.name, "gate_waiting");
|
|
4779
|
-
deps.tracker.updatePhaseProgress(issue.iid, spec.name, { status: "gate_waiting" });
|
|
4780
|
-
const gateEvent = spec.name === "uat" ? "uat:gateRequested" : "release:gateRequested";
|
|
4781
|
-
deps.eventBus.emitTyped(gateEvent, { issueIid: issue.iid });
|
|
4782
|
-
logger17.info("Async phase running (no awaitCompletion), pausing pipeline", { iid: issue.iid, phase: spec.name });
|
|
4783
|
-
return { serversStarted, paused: true };
|
|
5115
|
+
const result = await strategy.execute(execCtx, hooks);
|
|
5116
|
+
if (result.paused) {
|
|
5117
|
+
return { serversStarted: serversStarted || !!result.serversStarted, paused: true };
|
|
4784
5118
|
}
|
|
4785
|
-
if (
|
|
4786
|
-
|
|
4787
|
-
wtPlan.updatePhaseProgress(spec.name, "gate_waiting");
|
|
4788
|
-
deps.tracker.updatePhaseProgress(issue.iid, spec.name, { status: "gate_waiting" });
|
|
4789
|
-
deps.eventBus.emitTyped("release:gateRequested", { issueIid: issue.iid });
|
|
4790
|
-
logger17.info("Phase requested gate, pausing", { iid: issue.iid, phase: spec.name });
|
|
4791
|
-
return { serversStarted, paused: true };
|
|
5119
|
+
if (result.serversStarted) {
|
|
5120
|
+
serversStarted = true;
|
|
4792
5121
|
}
|
|
4793
5122
|
if (needsDeployment && !serversStarted && lifecycleManager.shouldDeployPreview(spec.name)) {
|
|
4794
5123
|
const ports = await deps.startPreviewServers(ctx.wtCtx, issue);
|
|
@@ -4801,191 +5130,48 @@ async function executePhaseLoop(ctx, deps, wtGit, wtPlan, wtGitMap) {
|
|
|
4801
5130
|
}
|
|
4802
5131
|
return { serversStarted, paused: false };
|
|
4803
5132
|
}
|
|
4804
|
-
|
|
4805
|
-
const { issue } = ctx;
|
|
4806
|
-
const
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
return finalOutcome;
|
|
4825
|
-
}
|
|
4826
|
-
const errMsg = finalOutcome.error?.message ?? "Unknown error";
|
|
4827
|
-
const shortErr = errMsg.slice(0, 200);
|
|
4828
|
-
const wasActive = finalOutcome.error?.wasActiveAtTimeout ?? false;
|
|
4829
|
-
wtPlan.updatePhaseProgress(spec.name, "failed", errMsg);
|
|
4830
|
-
deps.tracker.updatePhaseProgress(displayId, spec.name, { status: "failed" });
|
|
4831
|
-
if (wasActive) {
|
|
4832
|
-
deps.tracker.markFailedSoft(displayId, errMsg, "phase_running" /* PhaseRunning */);
|
|
4833
|
-
} else {
|
|
4834
|
-
deps.tracker.markFailed(displayId, errMsg, "phase_running" /* PhaseRunning */);
|
|
4835
|
-
}
|
|
4836
|
-
await safeComment(deps, issue.id, issueProgressComment(spec.name, "failed", shortErr));
|
|
4837
|
-
throw new AIExecutionError(spec.name, `Phase ${spec.name} failed: ${shortErr}`, {
|
|
4838
|
-
output: finalOutcome.error?.rawOutput ?? finalOutcome.output,
|
|
4839
|
-
exitCode: finalOutcome.exitCode ?? 1,
|
|
4840
|
-
isRetryable: finalOutcome.error?.isRetryable,
|
|
4841
|
-
wasActiveAtTimeout: wasActive
|
|
4842
|
-
});
|
|
4843
|
-
}
|
|
4844
|
-
function findPreviousAiPhaseIndex(phases, currentIdx) {
|
|
4845
|
-
for (let j = currentIdx - 1; j >= 0; j--) {
|
|
4846
|
-
if (phases[j].kind === "ai") return j;
|
|
4847
|
-
}
|
|
4848
|
-
return -1;
|
|
4849
|
-
}
|
|
4850
|
-
async function executeVerifyFixLoop(ctx, deps, wtGit, wtPlan, verifyPhaseIdx, buildPhaseIdx, wtGitMap) {
|
|
4851
|
-
const { issue, lifecycleManager, phaseCtx } = ctx;
|
|
4852
|
-
const maxIterations = deps.config.verifyFixLoop.maxIterations;
|
|
4853
|
-
const verifySpec = ctx.pipelineDef.phases[verifyPhaseIdx];
|
|
4854
|
-
deps.eventBus.emitTyped("verify:loopStarted", {
|
|
4855
|
-
issueIid: issue.iid,
|
|
4856
|
-
maxIterations
|
|
4857
|
-
});
|
|
4858
|
-
logger17.info("Verify-fix loop started", {
|
|
4859
|
-
iid: issue.iid,
|
|
4860
|
-
maxIterations,
|
|
4861
|
-
buildPhaseIdx
|
|
4862
|
-
});
|
|
4863
|
-
for (let iteration = 1; iteration <= maxIterations; iteration++) {
|
|
4864
|
-
if (isShuttingDown()) {
|
|
4865
|
-
throw new ServiceShutdownError();
|
|
4866
|
-
}
|
|
4867
|
-
logger17.info("Verify-fix loop iteration", {
|
|
4868
|
-
iteration,
|
|
4869
|
-
maxIterations,
|
|
4870
|
-
iid: issue.iid
|
|
4871
|
-
});
|
|
4872
|
-
updateHooksForPhase(verifySpec, ctx.pipelineDef, ctx, wtPlan);
|
|
4873
|
-
const verifyRunner = resolveVerifyRunner(deps);
|
|
4874
|
-
const verifyPhase = createPhase("verify", verifyRunner, wtGit, wtPlan, deps.config);
|
|
4875
|
-
if (wtGitMap) {
|
|
4876
|
-
verifyPhase.setWtGitMap(wtGitMap);
|
|
4877
|
-
}
|
|
4878
|
-
let verifyOutcome;
|
|
4879
|
-
try {
|
|
4880
|
-
verifyOutcome = await runPhaseWithLifecycle(
|
|
4881
|
-
verifyPhase,
|
|
4882
|
-
phaseCtx,
|
|
4883
|
-
verifySpec,
|
|
4884
|
-
ctx,
|
|
4885
|
-
deps,
|
|
4886
|
-
wtGit,
|
|
4887
|
-
wtPlan,
|
|
4888
|
-
wtGitMap
|
|
4889
|
-
);
|
|
4890
|
-
} catch (err) {
|
|
4891
|
-
logger17.warn("Verify phase execution failed", {
|
|
4892
|
-
iteration,
|
|
4893
|
-
iid: issue.iid,
|
|
4894
|
-
error: err.message
|
|
4895
|
-
});
|
|
4896
|
-
deps.eventBus.emitTyped("verify:iterationComplete", {
|
|
4897
|
-
issueIid: issue.iid,
|
|
4898
|
-
iteration,
|
|
4899
|
-
passed: false,
|
|
4900
|
-
failures: ["AI runner execution failed"]
|
|
4901
|
-
});
|
|
4902
|
-
if (iteration === maxIterations) {
|
|
4903
|
-
throw err;
|
|
4904
|
-
}
|
|
4905
|
-
if (buildPhaseIdx >= 0) {
|
|
4906
|
-
await executeBuildFix(ctx, deps, wtGit, wtPlan, buildPhaseIdx, {
|
|
4907
|
-
iteration,
|
|
4908
|
-
verifyFailures: ["AI runner execution failed: " + err.message],
|
|
4909
|
-
rawReport: ""
|
|
4910
|
-
}, wtGitMap);
|
|
5133
|
+
function healStaleProgress(ctx, deps, wtPlan, startIdx) {
|
|
5134
|
+
const { issue, pipelineDef, record } = ctx;
|
|
5135
|
+
const currentProgress = wtPlan.readProgress();
|
|
5136
|
+
if (currentProgress) {
|
|
5137
|
+
let patched = false;
|
|
5138
|
+
for (let i = 0; i < startIdx; i++) {
|
|
5139
|
+
const prevSpec = pipelineDef.phases[i];
|
|
5140
|
+
const pp = currentProgress.phases[prevSpec.name];
|
|
5141
|
+
if (pp && pp.status !== "completed") {
|
|
5142
|
+
logger22.warn("Fixing stale phase progress", {
|
|
5143
|
+
iid: issue.iid,
|
|
5144
|
+
phase: prevSpec.name,
|
|
5145
|
+
was: pp.status,
|
|
5146
|
+
now: "completed"
|
|
5147
|
+
});
|
|
5148
|
+
pp.status = "completed";
|
|
5149
|
+
if (!pp.completedAt) {
|
|
5150
|
+
pp.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5151
|
+
}
|
|
5152
|
+
patched = true;
|
|
4911
5153
|
}
|
|
4912
|
-
continue;
|
|
4913
|
-
}
|
|
4914
|
-
const report = verifyOutcome.data?.verifyReport;
|
|
4915
|
-
const passed = report ? report.passed : true;
|
|
4916
|
-
deps.eventBus.emitTyped("verify:iterationComplete", {
|
|
4917
|
-
issueIid: issue.iid,
|
|
4918
|
-
iteration,
|
|
4919
|
-
passed,
|
|
4920
|
-
failures: report?.failureReasons
|
|
4921
|
-
});
|
|
4922
|
-
if (passed) {
|
|
4923
|
-
logger17.info("Verify-fix loop passed", {
|
|
4924
|
-
iteration,
|
|
4925
|
-
iid: issue.iid
|
|
4926
|
-
});
|
|
4927
|
-
return;
|
|
4928
|
-
}
|
|
4929
|
-
logger17.info("Verify failed, issues found", {
|
|
4930
|
-
iteration,
|
|
4931
|
-
iid: issue.iid,
|
|
4932
|
-
failures: report?.failureReasons,
|
|
4933
|
-
todolistStats: report?.todolistStats
|
|
4934
|
-
});
|
|
4935
|
-
if (iteration === maxIterations) {
|
|
4936
|
-
deps.eventBus.emitTyped("verify:loopExhausted", {
|
|
4937
|
-
issueIid: issue.iid,
|
|
4938
|
-
totalIterations: iteration,
|
|
4939
|
-
failures: report?.failureReasons ?? []
|
|
4940
|
-
});
|
|
4941
|
-
const failMsg = `Verify-fix loop exhausted after ${maxIterations} iterations. Remaining issues: ${report?.failureReasons?.join("; ") ?? "unknown"}`;
|
|
4942
|
-
logger17.warn(failMsg, { iid: issue.iid });
|
|
4943
|
-
throw new AIExecutionError("verify", failMsg, {
|
|
4944
|
-
output: report?.rawReport ?? "",
|
|
4945
|
-
exitCode: 0
|
|
4946
|
-
});
|
|
4947
5154
|
}
|
|
4948
|
-
if (
|
|
4949
|
-
|
|
4950
|
-
iteration,
|
|
4951
|
-
verifyFailures: report?.failureReasons ?? [],
|
|
4952
|
-
rawReport: report?.rawReport ?? ""
|
|
4953
|
-
}, wtGitMap);
|
|
5155
|
+
if (patched) {
|
|
5156
|
+
wtPlan.writeProgress(currentProgress);
|
|
4954
5157
|
}
|
|
4955
5158
|
}
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
try {
|
|
4967
|
-
updateHooksForPhase(buildSpec, ctx.pipelineDef, ctx, wtPlan);
|
|
4968
|
-
const buildPhase = createPhase("build", deps.aiRunner, wtGit, wtPlan, deps.config);
|
|
4969
|
-
if (wtGitMap) {
|
|
4970
|
-
buildPhase.setWtGitMap(wtGitMap);
|
|
5159
|
+
if (record.phaseProgress) {
|
|
5160
|
+
for (let i = 0; i < startIdx; i++) {
|
|
5161
|
+
const prevSpec = pipelineDef.phases[i];
|
|
5162
|
+
const tp = record.phaseProgress[prevSpec.name];
|
|
5163
|
+
if (tp && tp.status !== "completed") {
|
|
5164
|
+
deps.tracker.updatePhaseProgress(issue.iid, prevSpec.name, {
|
|
5165
|
+
status: "completed",
|
|
5166
|
+
completedAt: tp.completedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
5167
|
+
});
|
|
5168
|
+
}
|
|
4971
5169
|
}
|
|
4972
|
-
await runPhaseWithLifecycle(
|
|
4973
|
-
buildPhase,
|
|
4974
|
-
phaseCtx,
|
|
4975
|
-
buildSpec,
|
|
4976
|
-
ctx,
|
|
4977
|
-
deps,
|
|
4978
|
-
wtGit,
|
|
4979
|
-
wtPlan,
|
|
4980
|
-
wtGitMap
|
|
4981
|
-
);
|
|
4982
|
-
} finally {
|
|
4983
|
-
delete phaseCtx.fixContext;
|
|
4984
5170
|
}
|
|
4985
5171
|
}
|
|
4986
5172
|
|
|
4987
5173
|
// src/orchestrator/steps/CompletionStep.ts
|
|
4988
|
-
var
|
|
5174
|
+
var logger23 = logger.child("CompletionStep");
|
|
4989
5175
|
async function executeCompletion(ctx, deps, phaseResult, _wtGitMap) {
|
|
4990
5176
|
const { issue, branchName, wtCtx } = ctx;
|
|
4991
5177
|
deps.emitProgress(issue.iid, "create_mr", t("orchestrator.createMrProgress"));
|
|
@@ -5017,7 +5203,7 @@ async function executeCompletion(ctx, deps, phaseResult, _wtGitMap) {
|
|
|
5017
5203
|
mrIid: void 0
|
|
5018
5204
|
});
|
|
5019
5205
|
} catch (err) {
|
|
5020
|
-
|
|
5206
|
+
logger23.warn("Failed to publish E2E screenshots", {
|
|
5021
5207
|
iid: issue.iid,
|
|
5022
5208
|
error: err.message
|
|
5023
5209
|
});
|
|
@@ -5037,19 +5223,19 @@ async function executeCompletion(ctx, deps, phaseResult, _wtGitMap) {
|
|
|
5037
5223
|
await deps.claimer.releaseClaim(issue.id, issue.iid, "completed");
|
|
5038
5224
|
}
|
|
5039
5225
|
if (phaseResult.serversStarted && deps.config.preview.keepAfterComplete) {
|
|
5040
|
-
|
|
5226
|
+
logger23.info("Preview servers kept running after completion", { iid: issue.iid });
|
|
5041
5227
|
} else {
|
|
5042
5228
|
deps.stopPreviewServers(issue.iid);
|
|
5043
5229
|
await deps.mainGitMutex.runExclusive(async () => {
|
|
5044
5230
|
if (wtCtx.workspace) {
|
|
5045
5231
|
await deps.workspaceManager.cleanupWorkspace(wtCtx.workspace);
|
|
5046
|
-
|
|
5232
|
+
logger23.info("Workspace cleaned up", { dir: wtCtx.workspace.workspaceRoot });
|
|
5047
5233
|
} else {
|
|
5048
5234
|
try {
|
|
5049
5235
|
await deps.mainGit.worktreeRemove(wtCtx.gitRootDir, true);
|
|
5050
|
-
|
|
5236
|
+
logger23.info("Worktree cleaned up", { dir: wtCtx.gitRootDir });
|
|
5051
5237
|
} catch (err) {
|
|
5052
|
-
|
|
5238
|
+
logger23.warn("Failed to cleanup worktree", {
|
|
5053
5239
|
dir: wtCtx.gitRootDir,
|
|
5054
5240
|
error: err.message
|
|
5055
5241
|
});
|
|
@@ -5057,16 +5243,16 @@ async function executeCompletion(ctx, deps, phaseResult, _wtGitMap) {
|
|
|
5057
5243
|
}
|
|
5058
5244
|
});
|
|
5059
5245
|
}
|
|
5060
|
-
|
|
5246
|
+
logger23.info("Issue processing completed", { iid: issue.iid });
|
|
5061
5247
|
}
|
|
5062
5248
|
|
|
5063
5249
|
// src/orchestrator/steps/FailureHandler.ts
|
|
5064
|
-
var
|
|
5250
|
+
var logger24 = logger.child("FailureHandler");
|
|
5065
5251
|
async function handleFailure(err, issue, wtCtx, deps) {
|
|
5066
5252
|
const errorMsg = err.message;
|
|
5067
5253
|
const isRetryable = err instanceof AIExecutionError ? err.isRetryable : true;
|
|
5068
5254
|
const wasActiveAtTimeout = err instanceof AIExecutionError && err.wasActiveAtTimeout;
|
|
5069
|
-
|
|
5255
|
+
logger24.error("Issue processing failed", { iid: issue.iid, error: errorMsg, isRetryable, wasActiveAtTimeout });
|
|
5070
5256
|
metrics.incCounter("iaf_issues_failed_total");
|
|
5071
5257
|
const currentRecord = deps.tracker.get(issue.iid);
|
|
5072
5258
|
const failedAtState = currentRecord?.state || "pending" /* Pending */;
|
|
@@ -5079,11 +5265,11 @@ async function handleFailure(err, issue, wtCtx, deps) {
|
|
|
5079
5265
|
}
|
|
5080
5266
|
}
|
|
5081
5267
|
if (wasReset) {
|
|
5082
|
-
|
|
5268
|
+
logger24.info("Issue was reset during processing, skipping failure marking", { iid: issue.iid });
|
|
5083
5269
|
throw err;
|
|
5084
5270
|
}
|
|
5085
5271
|
if (failedAtState === "paused" /* Paused */) {
|
|
5086
|
-
|
|
5272
|
+
logger24.info("Issue was paused during processing, skipping failure handling", { iid: issue.iid });
|
|
5087
5273
|
throw err;
|
|
5088
5274
|
}
|
|
5089
5275
|
try {
|
|
@@ -5105,7 +5291,7 @@ async function handleFailure(err, issue, wtCtx, deps) {
|
|
|
5105
5291
|
try {
|
|
5106
5292
|
await deps.claimer.releaseClaim(issue.id, issue.iid, "failed");
|
|
5107
5293
|
} catch (releaseErr) {
|
|
5108
|
-
|
|
5294
|
+
logger24.warn("Failed to release lock on failure", {
|
|
5109
5295
|
iid: issue.iid,
|
|
5110
5296
|
error: releaseErr.message
|
|
5111
5297
|
});
|
|
@@ -5113,7 +5299,7 @@ async function handleFailure(err, issue, wtCtx, deps) {
|
|
|
5113
5299
|
}
|
|
5114
5300
|
deps.stopPreviewServers(issue.iid);
|
|
5115
5301
|
const preservedDirs = wtCtx.workspace ? [wtCtx.workspace.primary.gitRootDir, ...wtCtx.workspace.associates.map((a) => a.gitRootDir)] : [wtCtx.gitRootDir];
|
|
5116
|
-
|
|
5302
|
+
logger24.info("Worktree(s) preserved for debugging", {
|
|
5117
5303
|
primary: wtCtx.gitRootDir,
|
|
5118
5304
|
all: preservedDirs
|
|
5119
5305
|
});
|
|
@@ -5122,7 +5308,7 @@ async function handleFailure(err, issue, wtCtx, deps) {
|
|
|
5122
5308
|
|
|
5123
5309
|
// src/orchestrator/PipelineOrchestrator.ts
|
|
5124
5310
|
var execFileAsync2 = promisify2(execFile2);
|
|
5125
|
-
var
|
|
5311
|
+
var logger25 = logger.child("PipelineOrchestrator");
|
|
5126
5312
|
var PipelineOrchestrator = class {
|
|
5127
5313
|
config;
|
|
5128
5314
|
gongfeng;
|
|
@@ -5152,7 +5338,7 @@ var PipelineOrchestrator = class {
|
|
|
5152
5338
|
setAIRunner(runner) {
|
|
5153
5339
|
this.aiRunner = runner;
|
|
5154
5340
|
this.conflictResolver = new ConflictResolver(runner);
|
|
5155
|
-
|
|
5341
|
+
logger25.info("AIRunner replaced via hot-reload");
|
|
5156
5342
|
}
|
|
5157
5343
|
constructor(config, gongfeng, git, aiRunner, tracker, supplementStore, mainGitMutex, eventBusInstance, wsConfig, tenantId, e2eAiRunner) {
|
|
5158
5344
|
this.config = config;
|
|
@@ -5170,7 +5356,7 @@ var PipelineOrchestrator = class {
|
|
|
5170
5356
|
this.pipelineDef = mode === "plan-mode" ? buildPlanModePipeline({ releaseEnabled: config.release.enabled, e2eEnabled: config.e2e.enabled }) : getPipelineDef(mode);
|
|
5171
5357
|
registerPipeline(this.pipelineDef);
|
|
5172
5358
|
this.lifecycleManager = createLifecycleManager(this.pipelineDef);
|
|
5173
|
-
|
|
5359
|
+
logger25.info("Pipeline mode resolved", { tenantId: this.tenantId, mode: this.pipelineDef.mode, aiMode: config.ai.mode });
|
|
5174
5360
|
this.portAllocator = new PortAllocator({
|
|
5175
5361
|
backendPortBase: config.e2e.backendPortBase,
|
|
5176
5362
|
frontendPortBase: config.e2e.frontendPortBase
|
|
@@ -5186,7 +5372,7 @@ var PipelineOrchestrator = class {
|
|
|
5186
5372
|
mainGitMutex: this.mainGitMutex,
|
|
5187
5373
|
gongfengApiUrl: config.gongfeng.apiUrl
|
|
5188
5374
|
});
|
|
5189
|
-
|
|
5375
|
+
logger25.info("WorkspaceManager initialized", {
|
|
5190
5376
|
tenantId: this.tenantId,
|
|
5191
5377
|
primary: effectiveWsConfig.primary.name,
|
|
5192
5378
|
associates: effectiveWsConfig.associates.map((a) => a.name)
|
|
@@ -5207,7 +5393,7 @@ var PipelineOrchestrator = class {
|
|
|
5207
5393
|
this.claimer = claimer;
|
|
5208
5394
|
}
|
|
5209
5395
|
async cleanupStaleState() {
|
|
5210
|
-
|
|
5396
|
+
logger25.info("Cleaning up stale worktree state...");
|
|
5211
5397
|
let cleaned = 0;
|
|
5212
5398
|
const repoGitRoot = this.config.project.gitRootDir;
|
|
5213
5399
|
try {
|
|
@@ -5220,7 +5406,7 @@ var PipelineOrchestrator = class {
|
|
|
5220
5406
|
try {
|
|
5221
5407
|
await fs11.access(gitFile);
|
|
5222
5408
|
} catch {
|
|
5223
|
-
|
|
5409
|
+
logger25.warn("Worktree corrupted (.git missing), force removing", { dir: wtDir });
|
|
5224
5410
|
await this.mainGit.worktreeRemove(wtDir, true).catch(() => {
|
|
5225
5411
|
});
|
|
5226
5412
|
await this.mainGit.worktreePrune();
|
|
@@ -5229,32 +5415,32 @@ var PipelineOrchestrator = class {
|
|
|
5229
5415
|
}
|
|
5230
5416
|
const wtGit = new GitOperations(wtDir);
|
|
5231
5417
|
if (await wtGit.isRebaseInProgress()) {
|
|
5232
|
-
|
|
5418
|
+
logger25.warn("Aborting residual rebase in worktree", { dir: wtDir });
|
|
5233
5419
|
await wtGit.rebaseAbort();
|
|
5234
5420
|
cleaned++;
|
|
5235
5421
|
}
|
|
5236
5422
|
const indexLock = path12.join(wtDir, ".git", "index.lock");
|
|
5237
5423
|
try {
|
|
5238
5424
|
await fs11.unlink(indexLock);
|
|
5239
|
-
|
|
5425
|
+
logger25.warn("Removed stale index.lock", { path: indexLock });
|
|
5240
5426
|
cleaned++;
|
|
5241
5427
|
} catch {
|
|
5242
5428
|
}
|
|
5243
5429
|
} catch (err) {
|
|
5244
|
-
|
|
5430
|
+
logger25.warn("Failed to clean worktree state", { dir: wtDir, error: err.message });
|
|
5245
5431
|
}
|
|
5246
5432
|
}
|
|
5247
5433
|
} catch (err) {
|
|
5248
|
-
|
|
5434
|
+
logger25.warn("Failed to list worktrees for cleanup", { error: err.message });
|
|
5249
5435
|
}
|
|
5250
5436
|
const mainIndexLock = path12.join(repoGitRoot, ".git", "index.lock");
|
|
5251
5437
|
try {
|
|
5252
5438
|
await fs11.unlink(mainIndexLock);
|
|
5253
|
-
|
|
5439
|
+
logger25.warn("Removed stale main repo index.lock", { path: mainIndexLock });
|
|
5254
5440
|
cleaned++;
|
|
5255
5441
|
} catch {
|
|
5256
5442
|
}
|
|
5257
|
-
|
|
5443
|
+
logger25.info("Stale state cleanup complete", { cleaned });
|
|
5258
5444
|
}
|
|
5259
5445
|
/**
|
|
5260
5446
|
* 重启后清理幽灵端口分配。
|
|
@@ -5267,7 +5453,7 @@ var PipelineOrchestrator = class {
|
|
|
5267
5453
|
for (const record of this.tracker.getAll()) {
|
|
5268
5454
|
if (record.ports) {
|
|
5269
5455
|
const iid = getIid(record);
|
|
5270
|
-
|
|
5456
|
+
logger25.info("Clearing stale port allocation after restart", { iid, ports: record.ports });
|
|
5271
5457
|
this.tracker.updateState(iid, record.state, {
|
|
5272
5458
|
ports: void 0,
|
|
5273
5459
|
previewStartedAt: void 0
|
|
@@ -5312,20 +5498,20 @@ var PipelineOrchestrator = class {
|
|
|
5312
5498
|
}
|
|
5313
5499
|
try {
|
|
5314
5500
|
await this.mainGit.worktreeRemove(wtCtx.gitRootDir, true);
|
|
5315
|
-
|
|
5501
|
+
logger25.info("Worktree cleaned up", { dir: wtCtx.gitRootDir });
|
|
5316
5502
|
} catch (err) {
|
|
5317
|
-
|
|
5503
|
+
logger25.warn("Failed to cleanup worktree", { dir: wtCtx.gitRootDir, error: err.message });
|
|
5318
5504
|
}
|
|
5319
5505
|
}
|
|
5320
5506
|
async installDependencies(workDir) {
|
|
5321
|
-
|
|
5507
|
+
logger25.info("Installing dependencies in worktree", { workDir });
|
|
5322
5508
|
const knowledge = getProjectKnowledge() ?? KNOWLEDGE_DEFAULTS;
|
|
5323
5509
|
const pkgMgr = knowledge.toolchain.packageManager.toLowerCase();
|
|
5324
5510
|
const isNodeProject = ["npm", "pnpm", "yarn", "bun"].some((m) => pkgMgr.includes(m));
|
|
5325
5511
|
if (isNodeProject) {
|
|
5326
5512
|
const ready = await this.ensureNodeModules(workDir);
|
|
5327
5513
|
if (ready) {
|
|
5328
|
-
|
|
5514
|
+
logger25.info("node_modules ready \u2014 skipping install");
|
|
5329
5515
|
return;
|
|
5330
5516
|
}
|
|
5331
5517
|
}
|
|
@@ -5338,10 +5524,10 @@ var PipelineOrchestrator = class {
|
|
|
5338
5524
|
maxBuffer: 10 * 1024 * 1024,
|
|
5339
5525
|
timeout: 3e5
|
|
5340
5526
|
});
|
|
5341
|
-
|
|
5527
|
+
logger25.info("Dependencies installed");
|
|
5342
5528
|
} catch (err) {
|
|
5343
5529
|
if (fallbackCmd) {
|
|
5344
|
-
|
|
5530
|
+
logger25.warn(`${installCmd} failed, retrying with fallback command`, {
|
|
5345
5531
|
error: err.message
|
|
5346
5532
|
});
|
|
5347
5533
|
const [fallbackBin, ...fallbackArgs] = fallbackCmd.split(/\s+/);
|
|
@@ -5351,14 +5537,14 @@ var PipelineOrchestrator = class {
|
|
|
5351
5537
|
maxBuffer: 10 * 1024 * 1024,
|
|
5352
5538
|
timeout: 3e5
|
|
5353
5539
|
});
|
|
5354
|
-
|
|
5540
|
+
logger25.info("Dependencies installed (fallback)");
|
|
5355
5541
|
} catch (retryErr) {
|
|
5356
|
-
|
|
5542
|
+
logger25.warn("Fallback install also failed", {
|
|
5357
5543
|
error: retryErr.message
|
|
5358
5544
|
});
|
|
5359
5545
|
}
|
|
5360
5546
|
} else {
|
|
5361
|
-
|
|
5547
|
+
logger25.warn("Install failed, no fallback configured", {
|
|
5362
5548
|
error: err.message
|
|
5363
5549
|
});
|
|
5364
5550
|
}
|
|
@@ -5368,7 +5554,7 @@ var PipelineOrchestrator = class {
|
|
|
5368
5554
|
const targetBin = path12.join(workDir, "node_modules", ".bin");
|
|
5369
5555
|
try {
|
|
5370
5556
|
await fs11.access(targetBin);
|
|
5371
|
-
|
|
5557
|
+
logger25.info("node_modules already complete (has .bin/)");
|
|
5372
5558
|
return true;
|
|
5373
5559
|
} catch {
|
|
5374
5560
|
}
|
|
@@ -5377,19 +5563,19 @@ var PipelineOrchestrator = class {
|
|
|
5377
5563
|
try {
|
|
5378
5564
|
await fs11.access(sourceNM);
|
|
5379
5565
|
} catch {
|
|
5380
|
-
|
|
5566
|
+
logger25.warn("Main repo node_modules not found, skipping seed", { sourceNM });
|
|
5381
5567
|
return false;
|
|
5382
5568
|
}
|
|
5383
|
-
|
|
5569
|
+
logger25.info("Seeding node_modules from main repo via reflink copy", { sourceNM, targetNM });
|
|
5384
5570
|
try {
|
|
5385
5571
|
await execFileAsync2("rm", ["-rf", targetNM], { timeout: 6e4 });
|
|
5386
5572
|
await execFileAsync2("cp", ["-a", "--reflink=auto", sourceNM, targetNM], {
|
|
5387
5573
|
timeout: 12e4
|
|
5388
5574
|
});
|
|
5389
|
-
|
|
5575
|
+
logger25.info("node_modules seeded from main repo");
|
|
5390
5576
|
return true;
|
|
5391
5577
|
} catch (err) {
|
|
5392
|
-
|
|
5578
|
+
logger25.warn("Failed to seed node_modules from main repo", {
|
|
5393
5579
|
error: err.message
|
|
5394
5580
|
});
|
|
5395
5581
|
return false;
|
|
@@ -5399,16 +5585,16 @@ var PipelineOrchestrator = class {
|
|
|
5399
5585
|
const record = this.tracker.get(issueIid);
|
|
5400
5586
|
if (!record) throw new IssueNotFoundError(issueIid);
|
|
5401
5587
|
const wtCtx = this.computeWorktreeContext(issueIid, record.branchName);
|
|
5402
|
-
|
|
5588
|
+
logger25.info("Restarting issue \u2014 cleaning context", { issueIid, branchName: record.branchName });
|
|
5403
5589
|
this.pendingActions.set(issueIid, "restart");
|
|
5404
5590
|
this.aiRunner.killByWorkDir(wtCtx.workDir);
|
|
5405
5591
|
this.e2eAiRunner?.killByWorkDir(wtCtx.workDir);
|
|
5406
5592
|
this.stopPreviewServers(issueIid);
|
|
5407
5593
|
try {
|
|
5408
5594
|
const deleted = await this.gongfeng.cleanupAgentNotes(getExternalId(record));
|
|
5409
|
-
|
|
5595
|
+
logger25.info("Agent notes cleaned up", { issueIid, deleted });
|
|
5410
5596
|
} catch (err) {
|
|
5411
|
-
|
|
5597
|
+
logger25.warn("Failed to cleanup agent notes", { issueIid, error: err.message });
|
|
5412
5598
|
}
|
|
5413
5599
|
await this.mainGitMutex.runExclusive(async () => {
|
|
5414
5600
|
await this.cleanupWorktree(wtCtx);
|
|
@@ -5425,19 +5611,19 @@ var PipelineOrchestrator = class {
|
|
|
5425
5611
|
await this.cleanupE2eOutputs(issueIid);
|
|
5426
5612
|
this.tracker.resetFull(issueIid);
|
|
5427
5613
|
this.pendingActions.delete(issueIid);
|
|
5428
|
-
|
|
5614
|
+
logger25.info("Issue restarted", { issueIid });
|
|
5429
5615
|
}
|
|
5430
5616
|
async cancelIssue(issueIid) {
|
|
5431
5617
|
const record = this.tracker.get(issueIid);
|
|
5432
5618
|
if (!record) throw new IssueNotFoundError(issueIid);
|
|
5433
5619
|
const wtCtx = this.computeWorktreeContext(issueIid, record.branchName);
|
|
5434
|
-
|
|
5620
|
+
logger25.info("Cancelling issue \u2014 cleaning all resources", { issueIid, branchName: record.branchName });
|
|
5435
5621
|
this.aiRunner.killByWorkDir(wtCtx.workDir);
|
|
5436
5622
|
this.stopPreviewServers(issueIid);
|
|
5437
5623
|
try {
|
|
5438
5624
|
await this.gongfeng.removeLabelsWithPrefix(getExternalId(record), "auto-finish");
|
|
5439
5625
|
} catch (err) {
|
|
5440
|
-
|
|
5626
|
+
logger25.warn("Failed to remove labels on cancel", { issueIid, error: err.message });
|
|
5441
5627
|
}
|
|
5442
5628
|
await this.mainGitMutex.runExclusive(async () => {
|
|
5443
5629
|
await this.cleanupWorktree(wtCtx);
|
|
@@ -5454,7 +5640,7 @@ var PipelineOrchestrator = class {
|
|
|
5454
5640
|
this.tracker.clearProcessingLock(issueIid);
|
|
5455
5641
|
this.tracker.updateState(issueIid, "skipped" /* Skipped */);
|
|
5456
5642
|
await this.cleanupE2eOutputs(issueIid);
|
|
5457
|
-
|
|
5643
|
+
logger25.info("Issue cancelled", { issueIid });
|
|
5458
5644
|
}
|
|
5459
5645
|
/**
|
|
5460
5646
|
* Remove the E2E output directory for an issue: {uatVendorDir}/outputs/issue-{iid}
|
|
@@ -5466,9 +5652,9 @@ var PipelineOrchestrator = class {
|
|
|
5466
5652
|
const outputDir = path12.join(abs, "outputs", `issue-${issueIid}`);
|
|
5467
5653
|
try {
|
|
5468
5654
|
await fs11.rm(outputDir, { recursive: true, force: true });
|
|
5469
|
-
|
|
5655
|
+
logger25.info("E2E outputs cleaned up", { issueIid, dir: outputDir });
|
|
5470
5656
|
} catch (err) {
|
|
5471
|
-
|
|
5657
|
+
logger25.warn("Failed to cleanup E2E outputs", { issueIid, dir: outputDir, error: err.message });
|
|
5472
5658
|
}
|
|
5473
5659
|
}
|
|
5474
5660
|
/**
|
|
@@ -5481,9 +5667,9 @@ var PipelineOrchestrator = class {
|
|
|
5481
5667
|
const wsRoot = this.workspaceManager.getWorkspaceRoot(issueIid);
|
|
5482
5668
|
try {
|
|
5483
5669
|
await fs11.rm(wsRoot, { recursive: true, force: true });
|
|
5484
|
-
|
|
5670
|
+
logger25.info("Workspace root cleaned up", { issueIid, dir: wsRoot });
|
|
5485
5671
|
} catch (err) {
|
|
5486
|
-
|
|
5672
|
+
logger25.warn("Failed to cleanup workspace root", { issueIid, dir: wsRoot, error: err.message });
|
|
5487
5673
|
}
|
|
5488
5674
|
}
|
|
5489
5675
|
retryFromPhase(issueIid, phase) {
|
|
@@ -5499,7 +5685,7 @@ var PipelineOrchestrator = class {
|
|
|
5499
5685
|
this.aiRunner.killByWorkDir(wtCtx.workDir);
|
|
5500
5686
|
}
|
|
5501
5687
|
this.e2eAiRunner?.killByWorkDir(wtCtx.workDir);
|
|
5502
|
-
|
|
5688
|
+
logger25.info("Retrying issue from phase", { issueIid, phase });
|
|
5503
5689
|
const ok = this.tracker.resetToPhase(issueIid, phase, issueDef);
|
|
5504
5690
|
if (!ok) {
|
|
5505
5691
|
throw new InvalidPhaseError(phase);
|
|
@@ -5526,7 +5712,7 @@ var PipelineOrchestrator = class {
|
|
|
5526
5712
|
} else {
|
|
5527
5713
|
this.tracker.pauseIssue(issueIid, record.currentPhase ?? "");
|
|
5528
5714
|
}
|
|
5529
|
-
|
|
5715
|
+
logger25.info("Issue abort requested", { issueIid, state: record.state });
|
|
5530
5716
|
}
|
|
5531
5717
|
continueIssue(issueIid) {
|
|
5532
5718
|
const record = this.tracker.get(issueIid);
|
|
@@ -5536,7 +5722,7 @@ var PipelineOrchestrator = class {
|
|
|
5536
5722
|
}
|
|
5537
5723
|
const issueDef = this.getIssueSpecificPipelineDef(record);
|
|
5538
5724
|
this.tracker.resumeFromPause(issueIid, issueDef, false);
|
|
5539
|
-
|
|
5725
|
+
logger25.info("Issue continued from pause", { issueIid });
|
|
5540
5726
|
}
|
|
5541
5727
|
redoPhase(issueIid) {
|
|
5542
5728
|
const record = this.tracker.get(issueIid);
|
|
@@ -5580,7 +5766,7 @@ var PipelineOrchestrator = class {
|
|
|
5580
5766
|
}
|
|
5581
5767
|
this.eventBus.emitTyped("issue:redone", { issueIid });
|
|
5582
5768
|
}
|
|
5583
|
-
|
|
5769
|
+
logger25.info("Issue redo requested", { issueIid, state: record.state });
|
|
5584
5770
|
}
|
|
5585
5771
|
/**
|
|
5586
5772
|
* 处理中止/重做的共享逻辑:
|
|
@@ -5653,7 +5839,7 @@ var PipelineOrchestrator = class {
|
|
|
5653
5839
|
async _processIssueImpl(issue) {
|
|
5654
5840
|
const branchName = `${this.config.project.branchPrefix}-${issue.iid}`;
|
|
5655
5841
|
const wtCtx = this.computeWorktreeContext(issue.iid, branchName);
|
|
5656
|
-
|
|
5842
|
+
logger25.info("Processing issue", {
|
|
5657
5843
|
iid: issue.iid,
|
|
5658
5844
|
title: issue.title,
|
|
5659
5845
|
branchName,
|
|
@@ -5760,7 +5946,7 @@ var PipelineOrchestrator = class {
|
|
|
5760
5946
|
title,
|
|
5761
5947
|
description
|
|
5762
5948
|
});
|
|
5763
|
-
|
|
5949
|
+
logger25.info("Merge request created successfully", {
|
|
5764
5950
|
iid: issue.iid,
|
|
5765
5951
|
mrIid: mr.iid,
|
|
5766
5952
|
mrUrl: mr.web_url
|
|
@@ -5768,7 +5954,7 @@ var PipelineOrchestrator = class {
|
|
|
5768
5954
|
return { url: mr.web_url, iid: mr.iid };
|
|
5769
5955
|
} catch (err) {
|
|
5770
5956
|
const errorMsg = err.message;
|
|
5771
|
-
|
|
5957
|
+
logger25.warn("Failed to create merge request, trying to find existing one", {
|
|
5772
5958
|
iid: issue.iid,
|
|
5773
5959
|
error: errorMsg
|
|
5774
5960
|
});
|
|
@@ -5785,7 +5971,7 @@ var PipelineOrchestrator = class {
|
|
|
5785
5971
|
this.config.project.baseBranch
|
|
5786
5972
|
);
|
|
5787
5973
|
if (existing) {
|
|
5788
|
-
|
|
5974
|
+
logger25.info("Found existing merge request", {
|
|
5789
5975
|
iid: issueIid,
|
|
5790
5976
|
mrIid: existing.iid,
|
|
5791
5977
|
mrUrl: existing.web_url
|
|
@@ -5793,7 +5979,7 @@ var PipelineOrchestrator = class {
|
|
|
5793
5979
|
return { url: existing.web_url, iid: existing.iid };
|
|
5794
5980
|
}
|
|
5795
5981
|
} catch (findErr) {
|
|
5796
|
-
|
|
5982
|
+
logger25.warn("Failed to find existing merge request", {
|
|
5797
5983
|
iid: issueIid,
|
|
5798
5984
|
error: findErr.message
|
|
5799
5985
|
});
|
|
@@ -5838,7 +6024,7 @@ var PipelineOrchestrator = class {
|
|
|
5838
6024
|
});
|
|
5839
6025
|
return ports;
|
|
5840
6026
|
} catch (err) {
|
|
5841
|
-
|
|
6027
|
+
logger25.error("Failed to start preview servers", {
|
|
5842
6028
|
iid: issue.iid,
|
|
5843
6029
|
error: err.message
|
|
5844
6030
|
});
|
|
@@ -5873,7 +6059,7 @@ E2E \u6D4B\u8BD5\u5C06\u5C1D\u8BD5\u4F7F\u7528 config.json \u4E2D\u7684\u9ED8\u8
|
|
|
5873
6059
|
await this.mainGitMutex.runExclusive(async () => {
|
|
5874
6060
|
await this.cleanupWorktree(wtCtx);
|
|
5875
6061
|
});
|
|
5876
|
-
|
|
6062
|
+
logger25.info("Preview stopped and worktree cleaned", { iid: issueIid });
|
|
5877
6063
|
}
|
|
5878
6064
|
async markDeployed(issueIid) {
|
|
5879
6065
|
const record = this.tracker.get(issueIid);
|
|
@@ -5890,7 +6076,7 @@ E2E \u6D4B\u8BD5\u5C06\u5C1D\u8BD5\u4F7F\u7528 config.json \u4E2D\u7684\u9ED8\u8
|
|
|
5890
6076
|
try {
|
|
5891
6077
|
await this.gongfeng.closeIssue(externalId);
|
|
5892
6078
|
} catch (err) {
|
|
5893
|
-
|
|
6079
|
+
logger25.warn("Failed to close issue on Gongfeng", { iid: issueIid, error: err.message });
|
|
5894
6080
|
}
|
|
5895
6081
|
try {
|
|
5896
6082
|
const issue = await this.gongfeng.getIssueDetail(externalId);
|
|
@@ -5898,10 +6084,10 @@ E2E \u6D4B\u8BD5\u5C06\u5C1D\u8BD5\u4F7F\u7528 config.json \u4E2D\u7684\u9ED8\u8
|
|
|
5898
6084
|
labels.push("auto-finish:deployed");
|
|
5899
6085
|
await this.gongfeng.updateIssueLabels(externalId, labels);
|
|
5900
6086
|
} catch (err) {
|
|
5901
|
-
|
|
6087
|
+
logger25.warn("Failed to update labels", { iid: issueIid, error: err.message });
|
|
5902
6088
|
}
|
|
5903
6089
|
this.tracker.updateState(issueIid, "deployed" /* Deployed */);
|
|
5904
|
-
|
|
6090
|
+
logger25.info("Issue marked as deployed", { iid: issueIid });
|
|
5905
6091
|
}
|
|
5906
6092
|
async restartPreview(issueIid) {
|
|
5907
6093
|
const record = this.tracker.get(issueIid);
|
|
@@ -5928,7 +6114,7 @@ E2E \u6D4B\u8BD5\u5C06\u5C1D\u8BD5\u4F7F\u7528 config.json \u4E2D\u7684\u9ED8\u8
|
|
|
5928
6114
|
throw err;
|
|
5929
6115
|
}
|
|
5930
6116
|
const url = this.buildPreviewUrl(issueIid);
|
|
5931
|
-
|
|
6117
|
+
logger25.info("Preview restarted", { iid: issueIid, url });
|
|
5932
6118
|
return url;
|
|
5933
6119
|
}
|
|
5934
6120
|
getPreviewHost() {
|
|
@@ -5961,7 +6147,7 @@ E2E \u6D4B\u8BD5\u5C06\u5C1D\u8BD5\u4F7F\u7528 config.json \u4E2D\u7684\u9ED8\u8
|
|
|
5961
6147
|
if (!record) throw new IssueNotFoundError(issueIid);
|
|
5962
6148
|
const baseBranch = this.config.project.baseBranch;
|
|
5963
6149
|
const branchName = record.branchName;
|
|
5964
|
-
|
|
6150
|
+
logger25.info("Starting conflict resolution", { issueIid, branchName, baseBranch });
|
|
5965
6151
|
this.tracker.updateState(issueIid, "resolving_conflict" /* ResolvingConflict */);
|
|
5966
6152
|
this.eventBus.emitTyped("conflict:started", { issueIid });
|
|
5967
6153
|
try {
|
|
@@ -5994,7 +6180,7 @@ E2E \u6D4B\u8BD5\u5C06\u5C1D\u8BD5\u4F7F\u7528 config.json \u4E2D\u7684\u9ED8\u8
|
|
|
5994
6180
|
});
|
|
5995
6181
|
}
|
|
5996
6182
|
});
|
|
5997
|
-
|
|
6183
|
+
logger25.info("Running verification after conflict resolution", { issueIid });
|
|
5998
6184
|
const wtPlan = new PlanPersistence(wtCtx.workDir, issueIid);
|
|
5999
6185
|
wtPlan.ensureDir();
|
|
6000
6186
|
const verifyPhase = createPhase("verify", this.aiRunner, wtGit, wtPlan, this.config);
|
|
@@ -6032,10 +6218,10 @@ E2E \u6D4B\u8BD5\u5C06\u5C1D\u8BD5\u4F7F\u7528 config.json \u4E2D\u7684\u9ED8\u8
|
|
|
6032
6218
|
} catch {
|
|
6033
6219
|
}
|
|
6034
6220
|
await this.commentOnMr(record.mrUrl, t("conflict.mrResolvedComment"));
|
|
6035
|
-
|
|
6221
|
+
logger25.info("Conflict resolution completed", { issueIid });
|
|
6036
6222
|
} catch (err) {
|
|
6037
6223
|
const errorMsg = err.message;
|
|
6038
|
-
|
|
6224
|
+
logger25.error("Conflict resolution failed", { issueIid, error: errorMsg });
|
|
6039
6225
|
try {
|
|
6040
6226
|
const wtGit = new GitOperations(wtCtx.gitRootDir);
|
|
6041
6227
|
if (await wtGit.isRebaseInProgress()) {
|
|
@@ -6065,7 +6251,7 @@ E2E \u6D4B\u8BD5\u5C06\u5C1D\u8BD5\u4F7F\u7528 config.json \u4E2D\u7684\u9ED8\u8
|
|
|
6065
6251
|
try {
|
|
6066
6252
|
await this.gongfeng.createMergeRequestNote(mrIid, body);
|
|
6067
6253
|
} catch (err) {
|
|
6068
|
-
|
|
6254
|
+
logger25.warn("Failed to comment on MR", { mrIid, error: err.message });
|
|
6069
6255
|
}
|
|
6070
6256
|
}
|
|
6071
6257
|
};
|
|
@@ -6141,7 +6327,7 @@ ${questions}
|
|
|
6141
6327
|
}
|
|
6142
6328
|
|
|
6143
6329
|
// src/services/BrainstormService.ts
|
|
6144
|
-
var
|
|
6330
|
+
var logger26 = logger.child("Brainstorm");
|
|
6145
6331
|
function agentConfigToAIConfig(agentCfg, timeoutMs) {
|
|
6146
6332
|
return {
|
|
6147
6333
|
mode: agentCfg.mode,
|
|
@@ -6177,7 +6363,7 @@ var BrainstormService = class {
|
|
|
6177
6363
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6178
6364
|
};
|
|
6179
6365
|
this.sessions.set(session.id, session);
|
|
6180
|
-
|
|
6366
|
+
logger26.info("Created brainstorm session", { sessionId: session.id });
|
|
6181
6367
|
return session;
|
|
6182
6368
|
}
|
|
6183
6369
|
getSession(id) {
|
|
@@ -6186,7 +6372,7 @@ var BrainstormService = class {
|
|
|
6186
6372
|
async generate(sessionId, onEvent) {
|
|
6187
6373
|
const session = this.requireSession(sessionId);
|
|
6188
6374
|
session.status = "generating";
|
|
6189
|
-
|
|
6375
|
+
logger26.info("Generating SDD", { sessionId });
|
|
6190
6376
|
const prompt = buildGeneratePrompt(session.transcript);
|
|
6191
6377
|
const result = await this.generatorRunner.run({
|
|
6192
6378
|
prompt,
|
|
@@ -6212,7 +6398,7 @@ var BrainstormService = class {
|
|
|
6212
6398
|
const session = this.requireSession(sessionId);
|
|
6213
6399
|
const roundNum = session.rounds.length + 1;
|
|
6214
6400
|
session.status = "reviewing";
|
|
6215
|
-
|
|
6401
|
+
logger26.info("Reviewing SDD", { sessionId, round: roundNum });
|
|
6216
6402
|
onEvent?.({ type: "round:start", data: { round: roundNum, phase: "review" }, round: roundNum });
|
|
6217
6403
|
const prompt = buildReviewPrompt(session.currentSdd, roundNum);
|
|
6218
6404
|
const result = await this.reviewerRunner.run({
|
|
@@ -6245,7 +6431,7 @@ var BrainstormService = class {
|
|
|
6245
6431
|
throw new Error("No review round to refine from");
|
|
6246
6432
|
}
|
|
6247
6433
|
session.status = "refining";
|
|
6248
|
-
|
|
6434
|
+
logger26.info("Refining SDD", { sessionId, round: currentRound.round });
|
|
6249
6435
|
const prompt = buildRefinePrompt(currentRound.questions);
|
|
6250
6436
|
const result = await this.generatorRunner.run({
|
|
6251
6437
|
prompt,
|
|
@@ -6332,4 +6518,4 @@ export {
|
|
|
6332
6518
|
PipelineOrchestrator,
|
|
6333
6519
|
BrainstormService
|
|
6334
6520
|
};
|
|
6335
|
-
//# sourceMappingURL=chunk-
|
|
6521
|
+
//# sourceMappingURL=chunk-WZGEYHCC.js.map
|