ai-whisper 0.1.3 → 0.2.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/README.md +18 -0
- package/dist/bin/broker-daemon.js +182 -11
- package/dist/bin/companion-agent.js +190 -19
- package/dist/bin/relay-monitor.js +179 -8
- package/dist/bin/whisper.js +280 -35
- package/dist/skills/ai-whisper-bugfix/SKILL.md +136 -0
- package/package.json +19 -3
- package/skills/ai-whisper-bugfix/SKILL.md +136 -0
|
@@ -2058,6 +2058,36 @@ Findings: (omit this block entirely if none)
|
|
|
2058
2058
|
Non-blocking risks:
|
|
2059
2059
|
- <quality risk that does NOT block this gate, or "None.">
|
|
2060
2060
|
--- end protocol ---`;
|
|
2061
|
+
var WORKFLOW_DIAGNOSIS_PROTOCOL = `--- ai-whisper diagnosis review protocol ---
|
|
2062
|
+
You are the gatekeeper for an ai-whisper complex-bug-fixing DIAGNOSIS gate. No human is in the loop. Do NOT trust the implementer's diagnosis \u2014 verify it yourself. The gate stays shut until YOU independently agree on the root cause and that the fix is net-safe.
|
|
2063
|
+
|
|
2064
|
+
Required procedure:
|
|
2065
|
+
1. Independently reproduce: re-run the implementer's reproduction yourself; do not trust pasted output. If YOU cannot reproduce it, that is a blocking finding (the repro is not real/reliable). Speculation from reading code paths is not a valid reproduction.
|
|
2066
|
+
2. Attack the causal claim: is the cause PROVEN by the evidence chain, or merely asserted? Could the named cause be a correlate, or a symptom of something deeper? Demand any missing link.
|
|
2067
|
+
3. Attack the fix (anti-whack-a-mole): does the proposed fix remove the root cause, or just this symptom's surface? Could the bug resurface through another path? Name it if so.
|
|
2068
|
+
4. Attack the blast radius: is every affected area/module/contract listed? Add what is missing.
|
|
2069
|
+
5. Attack residual risks: are the real foreseeable risks named, or hand-waved?
|
|
2070
|
+
6. Mutual-agreement gate: approve ONLY when you have independently confirmed the reproduction and agree the cause is proven and the fix is net-safe (fixes more than it risks). "Plausible" is not approval.
|
|
2071
|
+
|
|
2072
|
+
Severity: a blocking finding must tie to a concrete diagnosis-contract item (unreproducible repro, unproven cause, symptom-masking fix, incomplete blast radius, un-named risk). Suppress style/taste. Do NOT gag valuable non-contract risk signals \u2014 surface them under "Non-blocking risks:" (always last) so they reach the human at escalation/completion.
|
|
2073
|
+
|
|
2074
|
+
Findings vs escalation: a fixable defect in the diagnosis is a FINDING (loops back for revision) \u2014 do NOT say you "cannot proceed" for a defect you can describe. ESCALATE only when you genuinely cannot review (a required reproduction input is absent and is not the implementer's to supply).
|
|
2075
|
+
|
|
2076
|
+
Never reply with only a bare verdict; your full reply must be well over 100 characters.
|
|
2077
|
+
|
|
2078
|
+
Output format \u2014 the verdict line MUST come before the Non-blocking risks section, which is always LAST:
|
|
2079
|
+
Diagnosis review matrix:
|
|
2080
|
+
| Claim | Required evidence | Implementer evidence | Independently verified? | Result |
|
|
2081
|
+
| ... |
|
|
2082
|
+
|
|
2083
|
+
Findings: (omit this block entirely if none)
|
|
2084
|
+
- <blocking finding tied to an exact diagnosis-contract item, with evidence>
|
|
2085
|
+
|
|
2086
|
+
<verdict line: "Approved. <one or two sentences>" OR, when blocked/cannot-proceed, state you cannot proceed and why>
|
|
2087
|
+
|
|
2088
|
+
Non-blocking risks:
|
|
2089
|
+
- <risk that does NOT block this gate, or "None.">
|
|
2090
|
+
--- end protocol ---`;
|
|
2061
2091
|
var SDD_SPEC_REVIEW = "Review the spec at {specPath}. This is an autonomous workflow with no human in the loop.\n\n" + WORKFLOW_REVIEW_PROTOCOL;
|
|
2062
2092
|
var SDD_CODE_REVIEW = "Review the implementer's changes for this phase \u2014 the commits in {commitRange}. The upper bound is a LIVE `HEAD`: resolve it against the current repository at review time and INCLUDE any commits added during this review round (e.g. fixes for your prior findings); do not pin the review to an earlier tip. Verify against the spec's acceptance criteria and run the project's verification/tests. This is an autonomous workflow with no human in the loop.\n\n" + WORKFLOW_REVIEW_PROTOCOL;
|
|
2063
2093
|
var SPEC_DRIVEN_DEVELOPMENT = {
|
|
@@ -2178,13 +2208,113 @@ var RALPH_LOOP = {
|
|
|
2178
2208
|
}
|
|
2179
2209
|
]
|
|
2180
2210
|
};
|
|
2211
|
+
var BUGFIX_DIAGNOSIS_KICKOFF = `You are the implementer in an autonomous complex-bug-fixing workflow. No human is in the loop \u2014 never ask for confirmation, permission, or clarification; do the work yourself.
|
|
2212
|
+
|
|
2213
|
+
Read the bug report at {specPath}. A human (dev or QA) already observed this bug, so you must reproduce it too \u2014 do NOT theorize from reading code paths.
|
|
2214
|
+
|
|
2215
|
+
Write a diagnosis artifact to {diagnosisPath} (create the file) with these sections:
|
|
2216
|
+
1. Reproduction \u2014 an ACTUALLY OBSERVED reproduction you ran yourself. Strongly prefer a failing test (RED) failing for the right reason; commit that test in THIS phase. If the project supports e2e/real-browser (e.g. Playwright), use it. Only if no automated test can capture it, give command/log output WITH an explicit justification.
|
|
2217
|
+
2. Root cause \u2014 the causal chain symptom\u2192cause, each link backed by concrete evidence (stack trace, log line, failing assertion, bisect), not assertion.
|
|
2218
|
+
3. Proposed fix approach \u2014 what changes and WHY that removes the root cause rather than masking the symptom.
|
|
2219
|
+
4. Blast radius \u2014 every area/module/contract the fix could affect.
|
|
2220
|
+
5. Residual risks \u2014 foreseeable risks remaining after the fix.
|
|
2221
|
+
|
|
2222
|
+
Commit the RED reproduction test (do NOT commit {bugfixDir}; it is gitignored). End your handback with a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters \u2014 never hand back only a single word.`;
|
|
2223
|
+
var BUGFIX_DIAGNOSIS_REVIEW = "Review the diagnosis artifact at {diagnosisPath} for the bug reported at {specPath}. This is an autonomous workflow with no human in the loop.\n\n" + WORKFLOW_DIAGNOSIS_PROTOCOL;
|
|
2224
|
+
var BUGFIX_DIAGNOSIS_FIX = "Apply the reviewer's findings to the diagnosis at {diagnosisPath} now. This is an autonomous workflow \u2014 no human will respond. Make the edits yourself (re-reproduce if needed) and hand back the corrected diagnosis; never ask for confirmation, permission, or clarification. If you added or changed the RED reproduction test while addressing the findings, COMMIT it in this round (do NOT commit {bugfixDir}; it is gitignored) \u2014 the next phase reviews the committed change set, so an uncommitted repro update would be invisible to it. End your handback with a 1-2 sentence summary of what you changed; your reply must be at least two sentences, well over 100 characters \u2014 never hand back only a single word.";
|
|
2225
|
+
var BUGFIX_FIX_KICKOFF = `The diagnosis at {diagnosisPath} is APPROVED \u2014 treat it as ground truth (re-orient from it, not prior conversation). This is an autonomous workflow \u2014 no human will respond; do the work yourself.
|
|
2226
|
+
|
|
2227
|
+
1. Implement the fix per the APPROVED approach.
|
|
2228
|
+
2. Turn the reproduction GREEN \u2014 the failing test now passes for the right reason; if the repro was a non-test demonstration, re-run it and show the symptom is gone.
|
|
2229
|
+
3. Run the project's verification/test command PLUS targeted checks across the declared blast radius, including the full suite, to catch regressions.
|
|
2230
|
+
4. Commit the fix and any added happy-path/edge-case coverage tests (the RED test was committed in diagnosis). Do NOT commit {bugfixDir} (gitignored).
|
|
2231
|
+
|
|
2232
|
+
If you discover the approved cause was WRONG, do NOT silently switch to a different fix \u2014 the cause was the agreed premise of this run. Stop and hand back that you CANNOT PROCEED because the approved cause is wrong, explaining what the evidence now shows; do not deliver a fix built on a second, unreviewed theory. This halts the run and returns control to a human, who re-opens the diagnosis (a new run, or a resume after they adjust the report) \u2014 the workflow does not loop back to the diagnosis phase on its own. Otherwise, hand back the commit SHAs + verification output + a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters.`;
|
|
2233
|
+
var BUGFIX_FIX_REVIEW = "The implementer claims the fix for the bug at {specPath} is complete \u2014 the changes are commits {commitRange}; resolve the upper bound against LIVE HEAD and include fix-round commits. Verify against the APPROVED diagnosis at {diagnosisPath}. Independently re-run the reproduction (it must be GREEN) and the verification suite \u2014 do not trust pasted output. Confirm: the root cause is actually removed (not just relocated \u2014 anti-whack-a-mole); every declared blast-radius area is regression-free; residual risks are handled or explicitly accepted; and COVERAGE is adequate \u2014 every happy path has at least one covering test and edge cases are covered. A case that genuinely cannot be covered must be explicitly noted (not silently passed); thin coverage is a blocking finding. This is an autonomous workflow with no human in the loop.\n\n" + WORKFLOW_REVIEW_PROTOCOL;
|
|
2234
|
+
var BUGFIX_FIX_FIX = "Apply the reviewer's findings to commits {commitRange} now (amend or add commits). This is an autonomous workflow \u2014 no human will respond. Do the work yourself and hand back the updated commit SHAs and verification output; never ask for confirmation, permission, or clarification. End your handback with a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters.";
|
|
2235
|
+
var BUGFIX_POSTMORTEM_KICKOFF = "Write a post-mortem report to {postmortemPath} (create the file) for the bug fixed in this run. This is an autonomous workflow \u2014 no human will respond; do the work yourself. Recap: confirmed root cause; the fix applied; reproduction\u2192GREEN evidence; blast radius touched; coverage gaps explicitly listed (carried from the fix review); residual risks; and lessons learned. Do NOT commit {bugfixDir} (gitignored). End your handback with a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters.";
|
|
2236
|
+
var BUGFIX_POSTMORTEM_REVIEW = "Review the post-mortem report at {postmortemPath}. Confirm it faithfully reflects what actually happened in this run \u2014 confirmed cause, the fix, the noted coverage gaps, and residual risks are all present and honest. This is not a rubber stamp; gloss-overs or omissions are findings. This is an autonomous workflow with no human in the loop.\n\n" + WORKFLOW_REVIEW_PROTOCOL;
|
|
2237
|
+
var BUGFIX_POSTMORTEM_FIX = "Apply the reviewer's findings to the post-mortem at {postmortemPath} now. This is an autonomous workflow \u2014 no human will respond. Make the edits yourself and hand back the corrected report; never ask for confirmation, permission, or clarification. End your handback with a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters.";
|
|
2238
|
+
var COMPLEX_BUG_FIXING = {
|
|
2239
|
+
type: "complex-bug-fixing",
|
|
2240
|
+
displayName: "Complex Bug Fixing",
|
|
2241
|
+
description: "Reproduce \u2192 adversarially-gated diagnosis \u2192 fix & verify \u2192 post-mortem, for a reported bug whose root cause is unknown",
|
|
2242
|
+
defaultImplementer: "claude",
|
|
2243
|
+
defaultReviewer: "codex",
|
|
2244
|
+
phases: [
|
|
2245
|
+
{
|
|
2246
|
+
name: "diagnosis",
|
|
2247
|
+
implementerRole: "implementer",
|
|
2248
|
+
reviewerRole: "reviewer",
|
|
2249
|
+
maxRounds: 5,
|
|
2250
|
+
initialHandoffStep: "implement",
|
|
2251
|
+
kickoffTemplate: BUGFIX_DIAGNOSIS_KICKOFF,
|
|
2252
|
+
stepTemplates: {
|
|
2253
|
+
implement: BUGFIX_DIAGNOSIS_KICKOFF,
|
|
2254
|
+
review: BUGFIX_DIAGNOSIS_REVIEW,
|
|
2255
|
+
fix: BUGFIX_DIAGNOSIS_FIX
|
|
2256
|
+
},
|
|
2257
|
+
reviewMode: "phase-review",
|
|
2258
|
+
evaluatorPromptKey: "review-loop",
|
|
2259
|
+
artifactOut: { kind: "spec", pathTemplate: "{diagnosisPath}" },
|
|
2260
|
+
anchorCommitBaseOnEntry: true,
|
|
2261
|
+
renderFixTemplateOnFindings: true
|
|
2262
|
+
},
|
|
2263
|
+
{
|
|
2264
|
+
name: "fix-and-verify",
|
|
2265
|
+
implementerRole: "implementer",
|
|
2266
|
+
reviewerRole: "reviewer",
|
|
2267
|
+
maxRounds: 5,
|
|
2268
|
+
initialHandoffStep: "implement",
|
|
2269
|
+
kickoffTemplate: BUGFIX_FIX_KICKOFF,
|
|
2270
|
+
stepTemplates: {
|
|
2271
|
+
implement: BUGFIX_FIX_KICKOFF,
|
|
2272
|
+
review: BUGFIX_FIX_REVIEW,
|
|
2273
|
+
fix: BUGFIX_FIX_FIX
|
|
2274
|
+
},
|
|
2275
|
+
reviewMode: "acceptance-review",
|
|
2276
|
+
evaluatorPromptKey: "review-loop",
|
|
2277
|
+
artifactOut: { kind: "commit-range" },
|
|
2278
|
+
renderFixTemplateOnFindings: true
|
|
2279
|
+
},
|
|
2280
|
+
{
|
|
2281
|
+
name: "post-mortem",
|
|
2282
|
+
implementerRole: "implementer",
|
|
2283
|
+
reviewerRole: "reviewer",
|
|
2284
|
+
maxRounds: 3,
|
|
2285
|
+
initialHandoffStep: "implement",
|
|
2286
|
+
kickoffTemplate: BUGFIX_POSTMORTEM_KICKOFF,
|
|
2287
|
+
stepTemplates: {
|
|
2288
|
+
implement: BUGFIX_POSTMORTEM_KICKOFF,
|
|
2289
|
+
review: BUGFIX_POSTMORTEM_REVIEW,
|
|
2290
|
+
fix: BUGFIX_POSTMORTEM_FIX
|
|
2291
|
+
},
|
|
2292
|
+
reviewMode: "phase-review",
|
|
2293
|
+
evaluatorPromptKey: "review-loop",
|
|
2294
|
+
artifactOut: { kind: "spec", pathTemplate: "{postmortemPath}" },
|
|
2295
|
+
renderFixTemplateOnFindings: true
|
|
2296
|
+
}
|
|
2297
|
+
]
|
|
2298
|
+
};
|
|
2181
2299
|
var REGISTRY = {
|
|
2182
2300
|
[SPEC_DRIVEN_DEVELOPMENT.type]: SPEC_DRIVEN_DEVELOPMENT,
|
|
2183
|
-
[RALPH_LOOP.type]: RALPH_LOOP
|
|
2301
|
+
[RALPH_LOOP.type]: RALPH_LOOP,
|
|
2302
|
+
[COMPLEX_BUG_FIXING.type]: COMPLEX_BUG_FIXING
|
|
2184
2303
|
};
|
|
2185
2304
|
function ralphRunDir(workspaceRoot, workflowId) {
|
|
2186
2305
|
return join(workspaceRoot, ".ai-whisper", "ralph", workflowId);
|
|
2187
2306
|
}
|
|
2307
|
+
function bugfixRunDir(workspaceRoot, workflowId) {
|
|
2308
|
+
return join(workspaceRoot, ".ai-whisper", "bugfix", workflowId);
|
|
2309
|
+
}
|
|
2310
|
+
function bugfixPaths(workspaceRoot, workflowId) {
|
|
2311
|
+
const bugfixDir = bugfixRunDir(workspaceRoot, workflowId);
|
|
2312
|
+
return {
|
|
2313
|
+
bugfixDir,
|
|
2314
|
+
diagnosisPath: join(bugfixDir, "diagnosis.md"),
|
|
2315
|
+
postmortemPath: join(bugfixDir, "postmortem.md")
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2188
2318
|
function getWorkflowDefinition(type) {
|
|
2189
2319
|
return REGISTRY[type];
|
|
2190
2320
|
}
|
|
@@ -2404,12 +2534,16 @@ function createWorkflowControl(deps) {
|
|
|
2404
2534
|
const collab = getCollab(db, input.workflow.collabId);
|
|
2405
2535
|
const ralphDir = collab ? ralphRunDir(collab.workspaceRoot, input.workflow.workflowId) : "";
|
|
2406
2536
|
const reviewMode = useAcceptance ? "acceptance-review" : input.phase.reviewMode ?? "phase-review";
|
|
2537
|
+
const bf = collab ? bugfixPaths(collab.workspaceRoot, input.workflow.workflowId) : { bugfixDir: "", diagnosisPath: "", postmortemPath: "" };
|
|
2407
2538
|
return renderTemplate(tmpl, {
|
|
2408
2539
|
specPath: input.workflow.specPath,
|
|
2409
2540
|
planPath: safeDerivePlanPath(input.workflow.specPath, input.workflow.createdAt),
|
|
2410
2541
|
commitRange: liveReviewCommitRange(ctx),
|
|
2411
2542
|
ralphDir,
|
|
2412
|
-
reviewMode
|
|
2543
|
+
reviewMode,
|
|
2544
|
+
bugfixDir: bf.bugfixDir,
|
|
2545
|
+
diagnosisPath: bf.diagnosisPath,
|
|
2546
|
+
postmortemPath: bf.postmortemPath
|
|
2413
2547
|
});
|
|
2414
2548
|
}
|
|
2415
2549
|
function createContinuationHandoff(input) {
|
|
@@ -2446,12 +2580,16 @@ function createWorkflowControl(deps) {
|
|
|
2446
2580
|
const ctx = input.workflow.workflowContext;
|
|
2447
2581
|
const collab = getCollab(db, input.workflow.collabId);
|
|
2448
2582
|
const ralphDir = collab ? ralphRunDir(collab.workspaceRoot, input.workflow.workflowId) : "";
|
|
2583
|
+
const bf = collab ? bugfixPaths(collab.workspaceRoot, input.workflow.workflowId) : { bugfixDir: "", diagnosisPath: "", postmortemPath: "" };
|
|
2449
2584
|
const kickoffText = renderTemplate(phase.kickoffTemplate, {
|
|
2450
2585
|
specPath: input.workflow.specPath,
|
|
2451
2586
|
planPath: safeDerivePlanPath(input.workflow.specPath, input.workflow.createdAt),
|
|
2452
2587
|
commitRange: liveReviewCommitRange(ctx),
|
|
2453
2588
|
ralphDir,
|
|
2454
|
-
reviewMode: phase.reviewMode ?? "phase-review"
|
|
2589
|
+
reviewMode: phase.reviewMode ?? "phase-review",
|
|
2590
|
+
bugfixDir: bf.bugfixDir,
|
|
2591
|
+
diagnosisPath: bf.diagnosisPath,
|
|
2592
|
+
postmortemPath: bf.postmortemPath
|
|
2455
2593
|
});
|
|
2456
2594
|
const chainId = `relay_ch_${randomUUID().replace(/-/g, "").slice(0, 16)}`;
|
|
2457
2595
|
const phaseRunId = `wfp_${randomUUID().replace(/-/g, "").slice(0, 16)}`;
|
|
@@ -2709,15 +2847,19 @@ function createWorkflowControl(deps) {
|
|
|
2709
2847
|
} else if (normalized.verdict === "findings") {
|
|
2710
2848
|
const findingsText = input.followUpMessage ?? "Address the reviewer's findings.";
|
|
2711
2849
|
let fixRequestText;
|
|
2712
|
-
if (phase.repeatUntilComplete && phase.stepTemplates.fix) {
|
|
2850
|
+
if ((phase.repeatUntilComplete || phase.renderFixTemplateOnFindings) && phase.stepTemplates.fix) {
|
|
2713
2851
|
const ctx = workflow.workflowContext;
|
|
2714
2852
|
const collab = getCollab(db, workflow.collabId);
|
|
2715
2853
|
const ralphDir = collab ? ralphRunDir(collab.workspaceRoot, workflow.workflowId) : "";
|
|
2854
|
+
const bf = collab ? bugfixPaths(collab.workspaceRoot, workflow.workflowId) : { bugfixDir: "", diagnosisPath: "", postmortemPath: "" };
|
|
2716
2855
|
const fixTmpl = renderTemplate(phase.stepTemplates.fix, {
|
|
2717
2856
|
specPath: workflow.specPath,
|
|
2718
2857
|
planPath: safeDerivePlanPath(workflow.specPath, workflow.createdAt),
|
|
2719
2858
|
commitRange: liveReviewCommitRange(ctx),
|
|
2720
|
-
ralphDir
|
|
2859
|
+
ralphDir,
|
|
2860
|
+
bugfixDir: bf.bugfixDir,
|
|
2861
|
+
diagnosisPath: bf.diagnosisPath,
|
|
2862
|
+
postmortemPath: bf.postmortemPath
|
|
2721
2863
|
});
|
|
2722
2864
|
fixRequestText = `${fixTmpl}
|
|
2723
2865
|
|
|
@@ -4535,6 +4677,23 @@ function ensureRalphWorkspace(workspaceRoot, workflowId) {
|
|
|
4535
4677
|
return dir;
|
|
4536
4678
|
}
|
|
4537
4679
|
|
|
4680
|
+
// ../broker/dist/runtime/bugfix-setup.js
|
|
4681
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, existsSync as existsSync3 } from "node:fs";
|
|
4682
|
+
import { join as join3 } from "node:path";
|
|
4683
|
+
function ensureBugfixWorkspace(workspaceRoot, workflowId) {
|
|
4684
|
+
const dir = bugfixRunDir(workspaceRoot, workflowId);
|
|
4685
|
+
mkdirSync2(dir, { recursive: true });
|
|
4686
|
+
const ignorePath = join3(workspaceRoot, ".ai-whisper", ".gitignore");
|
|
4687
|
+
if (!existsSync3(ignorePath)) {
|
|
4688
|
+
writeFileSync2(ignorePath, "*\n");
|
|
4689
|
+
}
|
|
4690
|
+
const bugfixIgnorePath = join3(workspaceRoot, ".ai-whisper", "bugfix", ".gitignore");
|
|
4691
|
+
if (!existsSync3(bugfixIgnorePath)) {
|
|
4692
|
+
writeFileSync2(bugfixIgnorePath, "*\n");
|
|
4693
|
+
}
|
|
4694
|
+
return dir;
|
|
4695
|
+
}
|
|
4696
|
+
|
|
4538
4697
|
// ../broker/dist/runtime/workflow-driver.js
|
|
4539
4698
|
function createWorkflowDriver(deps) {
|
|
4540
4699
|
const { broker, headReader } = deps;
|
|
@@ -4605,7 +4764,7 @@ function createWorkflowDriver(deps) {
|
|
|
4605
4764
|
const sender = phase.initialHandoffStep === "review" ? implementerAgent : reviewerAgent;
|
|
4606
4765
|
const target = phase.initialHandoffStep === "review" ? reviewerAgent : implementerAgent;
|
|
4607
4766
|
let executionBaseHeadSha;
|
|
4608
|
-
if (phase.initialHandoffStep === "execute") {
|
|
4767
|
+
if (phase.initialHandoffStep === "execute" || phase.anchorCommitBaseOnEntry === true) {
|
|
4609
4768
|
try {
|
|
4610
4769
|
executionBaseHeadSha = await headReader.readHead(collab.workspaceRoot);
|
|
4611
4770
|
} catch (err) {
|
|
@@ -4628,6 +4787,15 @@ function createWorkflowDriver(deps) {
|
|
|
4628
4787
|
} else {
|
|
4629
4788
|
ralphDir = ralphRunDir(collab.workspaceRoot, workflowId);
|
|
4630
4789
|
}
|
|
4790
|
+
if (workflow.workflowType === "complex-bug-fixing") {
|
|
4791
|
+
try {
|
|
4792
|
+
ensureBugfixWorkspace(collab.workspaceRoot, workflowId);
|
|
4793
|
+
} catch (err) {
|
|
4794
|
+
broker.control.haltWorkflow({ workflowId, reason: `bugfix setup failed: ${String(err)}`, now });
|
|
4795
|
+
return;
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4798
|
+
const bugfix = bugfixPaths(collab.workspaceRoot, workflowId);
|
|
4631
4799
|
const ctx = workflow.workflowContext;
|
|
4632
4800
|
let planPath = workflow.specPath;
|
|
4633
4801
|
try {
|
|
@@ -4639,7 +4807,10 @@ function createWorkflowDriver(deps) {
|
|
|
4639
4807
|
planPath,
|
|
4640
4808
|
commitRange: ctx.commitRange ?? "HEAD",
|
|
4641
4809
|
ralphDir,
|
|
4642
|
-
reviewMode: phase.reviewMode ?? "phase-review"
|
|
4810
|
+
reviewMode: phase.reviewMode ?? "phase-review",
|
|
4811
|
+
bugfixDir: bugfix.bugfixDir,
|
|
4812
|
+
diagnosisPath: bugfix.diagnosisPath,
|
|
4813
|
+
postmortemPath: bugfix.postmortemPath
|
|
4643
4814
|
});
|
|
4644
4815
|
try {
|
|
4645
4816
|
broker.control.beginPhaseRun({
|
|
@@ -5429,14 +5600,14 @@ function createCompanionRuntime(input) {
|
|
|
5429
5600
|
|
|
5430
5601
|
// src/runtime/broker-artifact-service.ts
|
|
5431
5602
|
import fs from "node:fs";
|
|
5432
|
-
import { join as
|
|
5603
|
+
import { join as join5 } from "node:path";
|
|
5433
5604
|
|
|
5434
5605
|
// src/runtime/paths.ts
|
|
5435
5606
|
import os from "node:os";
|
|
5436
|
-
import { join as
|
|
5607
|
+
import { join as join4 } from "node:path";
|
|
5437
5608
|
function getLiveSessionBrokerTempRoot() {
|
|
5438
5609
|
const username = process.env["USER"] ?? process.env["USERNAME"] ?? "unknown";
|
|
5439
|
-
return
|
|
5610
|
+
return join4(os.tmpdir(), "ai-whisper", username, "live-session-broker");
|
|
5440
5611
|
}
|
|
5441
5612
|
|
|
5442
5613
|
// src/runtime/broker-artifact-service.ts
|
|
@@ -5479,9 +5650,9 @@ var BrokerArtifactService = class {
|
|
|
5479
5650
|
}
|
|
5480
5651
|
createArtifact(input) {
|
|
5481
5652
|
const dirName = buildDirName(input.now, input.workItemId);
|
|
5482
|
-
const artifactDirPath =
|
|
5483
|
-
const requestFilePath =
|
|
5484
|
-
const statusFilePath =
|
|
5653
|
+
const artifactDirPath = join5(this.tempRoot, dirName);
|
|
5654
|
+
const requestFilePath = join5(artifactDirPath, "request.json");
|
|
5655
|
+
const statusFilePath = join5(artifactDirPath, "status.json");
|
|
5485
5656
|
fs.mkdirSync(artifactDirPath, { recursive: true });
|
|
5486
5657
|
const requestData = {
|
|
5487
5658
|
schemaVersion: 1,
|
|
@@ -5560,9 +5731,9 @@ var BrokerArtifactService = class {
|
|
|
5560
5731
|
const entries = fs.readdirSync(this.tempRoot);
|
|
5561
5732
|
const now = Date.now();
|
|
5562
5733
|
for (const entry of entries) {
|
|
5563
|
-
const dirPath =
|
|
5734
|
+
const dirPath = join5(this.tempRoot, entry);
|
|
5564
5735
|
try {
|
|
5565
|
-
const statusPath =
|
|
5736
|
+
const statusPath = join5(dirPath, "status.json");
|
|
5566
5737
|
if (!fs.existsSync(statusPath)) continue;
|
|
5567
5738
|
const raw = fs.readFileSync(statusPath, "utf8");
|
|
5568
5739
|
const status = JSON.parse(raw);
|
|
@@ -5973,8 +6144,8 @@ function createClaudeLiveSession(input) {
|
|
|
5973
6144
|
|
|
5974
6145
|
// ../adapter-codex/dist/create-codex-provider.js
|
|
5975
6146
|
import { spawn as spawn3 } from "node:child_process";
|
|
5976
|
-
import { existsSync as
|
|
5977
|
-
import { join as
|
|
6147
|
+
import { existsSync as existsSync4, readFileSync, rmSync } from "node:fs";
|
|
6148
|
+
import { join as join6 } from "node:path";
|
|
5978
6149
|
|
|
5979
6150
|
// ../adapter-codex/dist/codex-prompt.js
|
|
5980
6151
|
function buildCodexPrompt(request) {
|
|
@@ -6109,7 +6280,7 @@ function createCodexProvider(config) {
|
|
|
6109
6280
|
},
|
|
6110
6281
|
handleWork(request, context) {
|
|
6111
6282
|
const prompt = context?.artifactHandle ? buildCodexFileBackedBrokerPrompt(context.artifactHandle.requestFilePath) : buildCodexPrompt(request);
|
|
6112
|
-
const outputLastMessagePath = context?.artifactHandle ?
|
|
6283
|
+
const outputLastMessagePath = context?.artifactHandle ? join6(context.artifactHandle.artifactDirPath, ".codex-last-message.json") : null;
|
|
6113
6284
|
const spawnArgs = outputLastMessagePath ? [...config.execArgs, "--output-last-message", outputLastMessagePath, prompt] : [...config.execArgs, prompt];
|
|
6114
6285
|
return new Promise((resolve2) => {
|
|
6115
6286
|
const child = spawn3(config.executable, spawnArgs, {
|
|
@@ -6146,7 +6317,7 @@ function createCodexProvider(config) {
|
|
|
6146
6317
|
});
|
|
6147
6318
|
return;
|
|
6148
6319
|
}
|
|
6149
|
-
if (outputLastMessagePath &&
|
|
6320
|
+
if (outputLastMessagePath && existsSync4(outputLastMessagePath)) {
|
|
6150
6321
|
try {
|
|
6151
6322
|
const fileReply = parseCodexOutput(readFileSync(outputLastMessagePath, "utf8"));
|
|
6152
6323
|
if (fileReply.kind !== "failure") {
|
|
@@ -2028,6 +2028,36 @@ Findings: (omit this block entirely if none)
|
|
|
2028
2028
|
Non-blocking risks:
|
|
2029
2029
|
- <quality risk that does NOT block this gate, or "None.">
|
|
2030
2030
|
--- end protocol ---`;
|
|
2031
|
+
var WORKFLOW_DIAGNOSIS_PROTOCOL = `--- ai-whisper diagnosis review protocol ---
|
|
2032
|
+
You are the gatekeeper for an ai-whisper complex-bug-fixing DIAGNOSIS gate. No human is in the loop. Do NOT trust the implementer's diagnosis \u2014 verify it yourself. The gate stays shut until YOU independently agree on the root cause and that the fix is net-safe.
|
|
2033
|
+
|
|
2034
|
+
Required procedure:
|
|
2035
|
+
1. Independently reproduce: re-run the implementer's reproduction yourself; do not trust pasted output. If YOU cannot reproduce it, that is a blocking finding (the repro is not real/reliable). Speculation from reading code paths is not a valid reproduction.
|
|
2036
|
+
2. Attack the causal claim: is the cause PROVEN by the evidence chain, or merely asserted? Could the named cause be a correlate, or a symptom of something deeper? Demand any missing link.
|
|
2037
|
+
3. Attack the fix (anti-whack-a-mole): does the proposed fix remove the root cause, or just this symptom's surface? Could the bug resurface through another path? Name it if so.
|
|
2038
|
+
4. Attack the blast radius: is every affected area/module/contract listed? Add what is missing.
|
|
2039
|
+
5. Attack residual risks: are the real foreseeable risks named, or hand-waved?
|
|
2040
|
+
6. Mutual-agreement gate: approve ONLY when you have independently confirmed the reproduction and agree the cause is proven and the fix is net-safe (fixes more than it risks). "Plausible" is not approval.
|
|
2041
|
+
|
|
2042
|
+
Severity: a blocking finding must tie to a concrete diagnosis-contract item (unreproducible repro, unproven cause, symptom-masking fix, incomplete blast radius, un-named risk). Suppress style/taste. Do NOT gag valuable non-contract risk signals \u2014 surface them under "Non-blocking risks:" (always last) so they reach the human at escalation/completion.
|
|
2043
|
+
|
|
2044
|
+
Findings vs escalation: a fixable defect in the diagnosis is a FINDING (loops back for revision) \u2014 do NOT say you "cannot proceed" for a defect you can describe. ESCALATE only when you genuinely cannot review (a required reproduction input is absent and is not the implementer's to supply).
|
|
2045
|
+
|
|
2046
|
+
Never reply with only a bare verdict; your full reply must be well over 100 characters.
|
|
2047
|
+
|
|
2048
|
+
Output format \u2014 the verdict line MUST come before the Non-blocking risks section, which is always LAST:
|
|
2049
|
+
Diagnosis review matrix:
|
|
2050
|
+
| Claim | Required evidence | Implementer evidence | Independently verified? | Result |
|
|
2051
|
+
| ... |
|
|
2052
|
+
|
|
2053
|
+
Findings: (omit this block entirely if none)
|
|
2054
|
+
- <blocking finding tied to an exact diagnosis-contract item, with evidence>
|
|
2055
|
+
|
|
2056
|
+
<verdict line: "Approved. <one or two sentences>" OR, when blocked/cannot-proceed, state you cannot proceed and why>
|
|
2057
|
+
|
|
2058
|
+
Non-blocking risks:
|
|
2059
|
+
- <risk that does NOT block this gate, or "None.">
|
|
2060
|
+
--- end protocol ---`;
|
|
2031
2061
|
var SDD_SPEC_REVIEW = "Review the spec at {specPath}. This is an autonomous workflow with no human in the loop.\n\n" + WORKFLOW_REVIEW_PROTOCOL;
|
|
2032
2062
|
var SDD_CODE_REVIEW = "Review the implementer's changes for this phase \u2014 the commits in {commitRange}. The upper bound is a LIVE `HEAD`: resolve it against the current repository at review time and INCLUDE any commits added during this review round (e.g. fixes for your prior findings); do not pin the review to an earlier tip. Verify against the spec's acceptance criteria and run the project's verification/tests. This is an autonomous workflow with no human in the loop.\n\n" + WORKFLOW_REVIEW_PROTOCOL;
|
|
2033
2063
|
var SPEC_DRIVEN_DEVELOPMENT = {
|
|
@@ -2148,13 +2178,113 @@ var RALPH_LOOP = {
|
|
|
2148
2178
|
}
|
|
2149
2179
|
]
|
|
2150
2180
|
};
|
|
2181
|
+
var BUGFIX_DIAGNOSIS_KICKOFF = `You are the implementer in an autonomous complex-bug-fixing workflow. No human is in the loop \u2014 never ask for confirmation, permission, or clarification; do the work yourself.
|
|
2182
|
+
|
|
2183
|
+
Read the bug report at {specPath}. A human (dev or QA) already observed this bug, so you must reproduce it too \u2014 do NOT theorize from reading code paths.
|
|
2184
|
+
|
|
2185
|
+
Write a diagnosis artifact to {diagnosisPath} (create the file) with these sections:
|
|
2186
|
+
1. Reproduction \u2014 an ACTUALLY OBSERVED reproduction you ran yourself. Strongly prefer a failing test (RED) failing for the right reason; commit that test in THIS phase. If the project supports e2e/real-browser (e.g. Playwright), use it. Only if no automated test can capture it, give command/log output WITH an explicit justification.
|
|
2187
|
+
2. Root cause \u2014 the causal chain symptom\u2192cause, each link backed by concrete evidence (stack trace, log line, failing assertion, bisect), not assertion.
|
|
2188
|
+
3. Proposed fix approach \u2014 what changes and WHY that removes the root cause rather than masking the symptom.
|
|
2189
|
+
4. Blast radius \u2014 every area/module/contract the fix could affect.
|
|
2190
|
+
5. Residual risks \u2014 foreseeable risks remaining after the fix.
|
|
2191
|
+
|
|
2192
|
+
Commit the RED reproduction test (do NOT commit {bugfixDir}; it is gitignored). End your handback with a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters \u2014 never hand back only a single word.`;
|
|
2193
|
+
var BUGFIX_DIAGNOSIS_REVIEW = "Review the diagnosis artifact at {diagnosisPath} for the bug reported at {specPath}. This is an autonomous workflow with no human in the loop.\n\n" + WORKFLOW_DIAGNOSIS_PROTOCOL;
|
|
2194
|
+
var BUGFIX_DIAGNOSIS_FIX = "Apply the reviewer's findings to the diagnosis at {diagnosisPath} now. This is an autonomous workflow \u2014 no human will respond. Make the edits yourself (re-reproduce if needed) and hand back the corrected diagnosis; never ask for confirmation, permission, or clarification. If you added or changed the RED reproduction test while addressing the findings, COMMIT it in this round (do NOT commit {bugfixDir}; it is gitignored) \u2014 the next phase reviews the committed change set, so an uncommitted repro update would be invisible to it. End your handback with a 1-2 sentence summary of what you changed; your reply must be at least two sentences, well over 100 characters \u2014 never hand back only a single word.";
|
|
2195
|
+
var BUGFIX_FIX_KICKOFF = `The diagnosis at {diagnosisPath} is APPROVED \u2014 treat it as ground truth (re-orient from it, not prior conversation). This is an autonomous workflow \u2014 no human will respond; do the work yourself.
|
|
2196
|
+
|
|
2197
|
+
1. Implement the fix per the APPROVED approach.
|
|
2198
|
+
2. Turn the reproduction GREEN \u2014 the failing test now passes for the right reason; if the repro was a non-test demonstration, re-run it and show the symptom is gone.
|
|
2199
|
+
3. Run the project's verification/test command PLUS targeted checks across the declared blast radius, including the full suite, to catch regressions.
|
|
2200
|
+
4. Commit the fix and any added happy-path/edge-case coverage tests (the RED test was committed in diagnosis). Do NOT commit {bugfixDir} (gitignored).
|
|
2201
|
+
|
|
2202
|
+
If you discover the approved cause was WRONG, do NOT silently switch to a different fix \u2014 the cause was the agreed premise of this run. Stop and hand back that you CANNOT PROCEED because the approved cause is wrong, explaining what the evidence now shows; do not deliver a fix built on a second, unreviewed theory. This halts the run and returns control to a human, who re-opens the diagnosis (a new run, or a resume after they adjust the report) \u2014 the workflow does not loop back to the diagnosis phase on its own. Otherwise, hand back the commit SHAs + verification output + a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters.`;
|
|
2203
|
+
var BUGFIX_FIX_REVIEW = "The implementer claims the fix for the bug at {specPath} is complete \u2014 the changes are commits {commitRange}; resolve the upper bound against LIVE HEAD and include fix-round commits. Verify against the APPROVED diagnosis at {diagnosisPath}. Independently re-run the reproduction (it must be GREEN) and the verification suite \u2014 do not trust pasted output. Confirm: the root cause is actually removed (not just relocated \u2014 anti-whack-a-mole); every declared blast-radius area is regression-free; residual risks are handled or explicitly accepted; and COVERAGE is adequate \u2014 every happy path has at least one covering test and edge cases are covered. A case that genuinely cannot be covered must be explicitly noted (not silently passed); thin coverage is a blocking finding. This is an autonomous workflow with no human in the loop.\n\n" + WORKFLOW_REVIEW_PROTOCOL;
|
|
2204
|
+
var BUGFIX_FIX_FIX = "Apply the reviewer's findings to commits {commitRange} now (amend or add commits). This is an autonomous workflow \u2014 no human will respond. Do the work yourself and hand back the updated commit SHAs and verification output; never ask for confirmation, permission, or clarification. End your handback with a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters.";
|
|
2205
|
+
var BUGFIX_POSTMORTEM_KICKOFF = "Write a post-mortem report to {postmortemPath} (create the file) for the bug fixed in this run. This is an autonomous workflow \u2014 no human will respond; do the work yourself. Recap: confirmed root cause; the fix applied; reproduction\u2192GREEN evidence; blast radius touched; coverage gaps explicitly listed (carried from the fix review); residual risks; and lessons learned. Do NOT commit {bugfixDir} (gitignored). End your handback with a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters.";
|
|
2206
|
+
var BUGFIX_POSTMORTEM_REVIEW = "Review the post-mortem report at {postmortemPath}. Confirm it faithfully reflects what actually happened in this run \u2014 confirmed cause, the fix, the noted coverage gaps, and residual risks are all present and honest. This is not a rubber stamp; gloss-overs or omissions are findings. This is an autonomous workflow with no human in the loop.\n\n" + WORKFLOW_REVIEW_PROTOCOL;
|
|
2207
|
+
var BUGFIX_POSTMORTEM_FIX = "Apply the reviewer's findings to the post-mortem at {postmortemPath} now. This is an autonomous workflow \u2014 no human will respond. Make the edits yourself and hand back the corrected report; never ask for confirmation, permission, or clarification. End your handback with a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters.";
|
|
2208
|
+
var COMPLEX_BUG_FIXING = {
|
|
2209
|
+
type: "complex-bug-fixing",
|
|
2210
|
+
displayName: "Complex Bug Fixing",
|
|
2211
|
+
description: "Reproduce \u2192 adversarially-gated diagnosis \u2192 fix & verify \u2192 post-mortem, for a reported bug whose root cause is unknown",
|
|
2212
|
+
defaultImplementer: "claude",
|
|
2213
|
+
defaultReviewer: "codex",
|
|
2214
|
+
phases: [
|
|
2215
|
+
{
|
|
2216
|
+
name: "diagnosis",
|
|
2217
|
+
implementerRole: "implementer",
|
|
2218
|
+
reviewerRole: "reviewer",
|
|
2219
|
+
maxRounds: 5,
|
|
2220
|
+
initialHandoffStep: "implement",
|
|
2221
|
+
kickoffTemplate: BUGFIX_DIAGNOSIS_KICKOFF,
|
|
2222
|
+
stepTemplates: {
|
|
2223
|
+
implement: BUGFIX_DIAGNOSIS_KICKOFF,
|
|
2224
|
+
review: BUGFIX_DIAGNOSIS_REVIEW,
|
|
2225
|
+
fix: BUGFIX_DIAGNOSIS_FIX
|
|
2226
|
+
},
|
|
2227
|
+
reviewMode: "phase-review",
|
|
2228
|
+
evaluatorPromptKey: "review-loop",
|
|
2229
|
+
artifactOut: { kind: "spec", pathTemplate: "{diagnosisPath}" },
|
|
2230
|
+
anchorCommitBaseOnEntry: true,
|
|
2231
|
+
renderFixTemplateOnFindings: true
|
|
2232
|
+
},
|
|
2233
|
+
{
|
|
2234
|
+
name: "fix-and-verify",
|
|
2235
|
+
implementerRole: "implementer",
|
|
2236
|
+
reviewerRole: "reviewer",
|
|
2237
|
+
maxRounds: 5,
|
|
2238
|
+
initialHandoffStep: "implement",
|
|
2239
|
+
kickoffTemplate: BUGFIX_FIX_KICKOFF,
|
|
2240
|
+
stepTemplates: {
|
|
2241
|
+
implement: BUGFIX_FIX_KICKOFF,
|
|
2242
|
+
review: BUGFIX_FIX_REVIEW,
|
|
2243
|
+
fix: BUGFIX_FIX_FIX
|
|
2244
|
+
},
|
|
2245
|
+
reviewMode: "acceptance-review",
|
|
2246
|
+
evaluatorPromptKey: "review-loop",
|
|
2247
|
+
artifactOut: { kind: "commit-range" },
|
|
2248
|
+
renderFixTemplateOnFindings: true
|
|
2249
|
+
},
|
|
2250
|
+
{
|
|
2251
|
+
name: "post-mortem",
|
|
2252
|
+
implementerRole: "implementer",
|
|
2253
|
+
reviewerRole: "reviewer",
|
|
2254
|
+
maxRounds: 3,
|
|
2255
|
+
initialHandoffStep: "implement",
|
|
2256
|
+
kickoffTemplate: BUGFIX_POSTMORTEM_KICKOFF,
|
|
2257
|
+
stepTemplates: {
|
|
2258
|
+
implement: BUGFIX_POSTMORTEM_KICKOFF,
|
|
2259
|
+
review: BUGFIX_POSTMORTEM_REVIEW,
|
|
2260
|
+
fix: BUGFIX_POSTMORTEM_FIX
|
|
2261
|
+
},
|
|
2262
|
+
reviewMode: "phase-review",
|
|
2263
|
+
evaluatorPromptKey: "review-loop",
|
|
2264
|
+
artifactOut: { kind: "spec", pathTemplate: "{postmortemPath}" },
|
|
2265
|
+
renderFixTemplateOnFindings: true
|
|
2266
|
+
}
|
|
2267
|
+
]
|
|
2268
|
+
};
|
|
2151
2269
|
var REGISTRY = {
|
|
2152
2270
|
[SPEC_DRIVEN_DEVELOPMENT.type]: SPEC_DRIVEN_DEVELOPMENT,
|
|
2153
|
-
[RALPH_LOOP.type]: RALPH_LOOP
|
|
2271
|
+
[RALPH_LOOP.type]: RALPH_LOOP,
|
|
2272
|
+
[COMPLEX_BUG_FIXING.type]: COMPLEX_BUG_FIXING
|
|
2154
2273
|
};
|
|
2155
2274
|
function ralphRunDir(workspaceRoot, workflowId) {
|
|
2156
2275
|
return join(workspaceRoot, ".ai-whisper", "ralph", workflowId);
|
|
2157
2276
|
}
|
|
2277
|
+
function bugfixRunDir(workspaceRoot, workflowId) {
|
|
2278
|
+
return join(workspaceRoot, ".ai-whisper", "bugfix", workflowId);
|
|
2279
|
+
}
|
|
2280
|
+
function bugfixPaths(workspaceRoot, workflowId) {
|
|
2281
|
+
const bugfixDir = bugfixRunDir(workspaceRoot, workflowId);
|
|
2282
|
+
return {
|
|
2283
|
+
bugfixDir,
|
|
2284
|
+
diagnosisPath: join(bugfixDir, "diagnosis.md"),
|
|
2285
|
+
postmortemPath: join(bugfixDir, "postmortem.md")
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2158
2288
|
function getWorkflowDefinition(type) {
|
|
2159
2289
|
return REGISTRY[type];
|
|
2160
2290
|
}
|
|
@@ -2374,12 +2504,16 @@ function createWorkflowControl(deps) {
|
|
|
2374
2504
|
const collab = getCollab(db, input.workflow.collabId);
|
|
2375
2505
|
const ralphDir = collab ? ralphRunDir(collab.workspaceRoot, input.workflow.workflowId) : "";
|
|
2376
2506
|
const reviewMode = useAcceptance ? "acceptance-review" : input.phase.reviewMode ?? "phase-review";
|
|
2507
|
+
const bf = collab ? bugfixPaths(collab.workspaceRoot, input.workflow.workflowId) : { bugfixDir: "", diagnosisPath: "", postmortemPath: "" };
|
|
2377
2508
|
return renderTemplate(tmpl, {
|
|
2378
2509
|
specPath: input.workflow.specPath,
|
|
2379
2510
|
planPath: safeDerivePlanPath(input.workflow.specPath, input.workflow.createdAt),
|
|
2380
2511
|
commitRange: liveReviewCommitRange(ctx),
|
|
2381
2512
|
ralphDir,
|
|
2382
|
-
reviewMode
|
|
2513
|
+
reviewMode,
|
|
2514
|
+
bugfixDir: bf.bugfixDir,
|
|
2515
|
+
diagnosisPath: bf.diagnosisPath,
|
|
2516
|
+
postmortemPath: bf.postmortemPath
|
|
2383
2517
|
});
|
|
2384
2518
|
}
|
|
2385
2519
|
function createContinuationHandoff(input) {
|
|
@@ -2416,12 +2550,16 @@ function createWorkflowControl(deps) {
|
|
|
2416
2550
|
const ctx = input.workflow.workflowContext;
|
|
2417
2551
|
const collab = getCollab(db, input.workflow.collabId);
|
|
2418
2552
|
const ralphDir = collab ? ralphRunDir(collab.workspaceRoot, input.workflow.workflowId) : "";
|
|
2553
|
+
const bf = collab ? bugfixPaths(collab.workspaceRoot, input.workflow.workflowId) : { bugfixDir: "", diagnosisPath: "", postmortemPath: "" };
|
|
2419
2554
|
const kickoffText = renderTemplate(phase.kickoffTemplate, {
|
|
2420
2555
|
specPath: input.workflow.specPath,
|
|
2421
2556
|
planPath: safeDerivePlanPath(input.workflow.specPath, input.workflow.createdAt),
|
|
2422
2557
|
commitRange: liveReviewCommitRange(ctx),
|
|
2423
2558
|
ralphDir,
|
|
2424
|
-
reviewMode: phase.reviewMode ?? "phase-review"
|
|
2559
|
+
reviewMode: phase.reviewMode ?? "phase-review",
|
|
2560
|
+
bugfixDir: bf.bugfixDir,
|
|
2561
|
+
diagnosisPath: bf.diagnosisPath,
|
|
2562
|
+
postmortemPath: bf.postmortemPath
|
|
2425
2563
|
});
|
|
2426
2564
|
const chainId = `relay_ch_${randomUUID().replace(/-/g, "").slice(0, 16)}`;
|
|
2427
2565
|
const phaseRunId = `wfp_${randomUUID().replace(/-/g, "").slice(0, 16)}`;
|
|
@@ -2679,15 +2817,19 @@ function createWorkflowControl(deps) {
|
|
|
2679
2817
|
} else if (normalized.verdict === "findings") {
|
|
2680
2818
|
const findingsText = input.followUpMessage ?? "Address the reviewer's findings.";
|
|
2681
2819
|
let fixRequestText;
|
|
2682
|
-
if (phase.repeatUntilComplete && phase.stepTemplates.fix) {
|
|
2820
|
+
if ((phase.repeatUntilComplete || phase.renderFixTemplateOnFindings) && phase.stepTemplates.fix) {
|
|
2683
2821
|
const ctx = workflow.workflowContext;
|
|
2684
2822
|
const collab = getCollab(db, workflow.collabId);
|
|
2685
2823
|
const ralphDir = collab ? ralphRunDir(collab.workspaceRoot, workflow.workflowId) : "";
|
|
2824
|
+
const bf = collab ? bugfixPaths(collab.workspaceRoot, workflow.workflowId) : { bugfixDir: "", diagnosisPath: "", postmortemPath: "" };
|
|
2686
2825
|
const fixTmpl = renderTemplate(phase.stepTemplates.fix, {
|
|
2687
2826
|
specPath: workflow.specPath,
|
|
2688
2827
|
planPath: safeDerivePlanPath(workflow.specPath, workflow.createdAt),
|
|
2689
2828
|
commitRange: liveReviewCommitRange(ctx),
|
|
2690
|
-
ralphDir
|
|
2829
|
+
ralphDir,
|
|
2830
|
+
bugfixDir: bf.bugfixDir,
|
|
2831
|
+
diagnosisPath: bf.diagnosisPath,
|
|
2832
|
+
postmortemPath: bf.postmortemPath
|
|
2691
2833
|
});
|
|
2692
2834
|
fixRequestText = `${fixTmpl}
|
|
2693
2835
|
|
|
@@ -4509,6 +4651,23 @@ function ensureRalphWorkspace(workspaceRoot, workflowId) {
|
|
|
4509
4651
|
return dir;
|
|
4510
4652
|
}
|
|
4511
4653
|
|
|
4654
|
+
// ../broker/dist/runtime/bugfix-setup.js
|
|
4655
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, existsSync as existsSync3 } from "node:fs";
|
|
4656
|
+
import { join as join3 } from "node:path";
|
|
4657
|
+
function ensureBugfixWorkspace(workspaceRoot, workflowId) {
|
|
4658
|
+
const dir = bugfixRunDir(workspaceRoot, workflowId);
|
|
4659
|
+
mkdirSync2(dir, { recursive: true });
|
|
4660
|
+
const ignorePath = join3(workspaceRoot, ".ai-whisper", ".gitignore");
|
|
4661
|
+
if (!existsSync3(ignorePath)) {
|
|
4662
|
+
writeFileSync2(ignorePath, "*\n");
|
|
4663
|
+
}
|
|
4664
|
+
const bugfixIgnorePath = join3(workspaceRoot, ".ai-whisper", "bugfix", ".gitignore");
|
|
4665
|
+
if (!existsSync3(bugfixIgnorePath)) {
|
|
4666
|
+
writeFileSync2(bugfixIgnorePath, "*\n");
|
|
4667
|
+
}
|
|
4668
|
+
return dir;
|
|
4669
|
+
}
|
|
4670
|
+
|
|
4512
4671
|
// ../broker/dist/runtime/workflow-driver.js
|
|
4513
4672
|
function createWorkflowDriver(deps) {
|
|
4514
4673
|
const { broker, headReader } = deps;
|
|
@@ -4579,7 +4738,7 @@ function createWorkflowDriver(deps) {
|
|
|
4579
4738
|
const sender = phase.initialHandoffStep === "review" ? implementerAgent : reviewerAgent;
|
|
4580
4739
|
const target = phase.initialHandoffStep === "review" ? reviewerAgent : implementerAgent;
|
|
4581
4740
|
let executionBaseHeadSha;
|
|
4582
|
-
if (phase.initialHandoffStep === "execute") {
|
|
4741
|
+
if (phase.initialHandoffStep === "execute" || phase.anchorCommitBaseOnEntry === true) {
|
|
4583
4742
|
try {
|
|
4584
4743
|
executionBaseHeadSha = await headReader.readHead(collab.workspaceRoot);
|
|
4585
4744
|
} catch (err) {
|
|
@@ -4602,6 +4761,15 @@ function createWorkflowDriver(deps) {
|
|
|
4602
4761
|
} else {
|
|
4603
4762
|
ralphDir = ralphRunDir(collab.workspaceRoot, workflowId);
|
|
4604
4763
|
}
|
|
4764
|
+
if (workflow.workflowType === "complex-bug-fixing") {
|
|
4765
|
+
try {
|
|
4766
|
+
ensureBugfixWorkspace(collab.workspaceRoot, workflowId);
|
|
4767
|
+
} catch (err) {
|
|
4768
|
+
broker.control.haltWorkflow({ workflowId, reason: `bugfix setup failed: ${String(err)}`, now });
|
|
4769
|
+
return;
|
|
4770
|
+
}
|
|
4771
|
+
}
|
|
4772
|
+
const bugfix = bugfixPaths(collab.workspaceRoot, workflowId);
|
|
4605
4773
|
const ctx = workflow.workflowContext;
|
|
4606
4774
|
let planPath = workflow.specPath;
|
|
4607
4775
|
try {
|
|
@@ -4613,7 +4781,10 @@ function createWorkflowDriver(deps) {
|
|
|
4613
4781
|
planPath,
|
|
4614
4782
|
commitRange: ctx.commitRange ?? "HEAD",
|
|
4615
4783
|
ralphDir,
|
|
4616
|
-
reviewMode: phase.reviewMode ?? "phase-review"
|
|
4784
|
+
reviewMode: phase.reviewMode ?? "phase-review",
|
|
4785
|
+
bugfixDir: bugfix.bugfixDir,
|
|
4786
|
+
diagnosisPath: bugfix.diagnosisPath,
|
|
4787
|
+
postmortemPath: bugfix.postmortemPath
|
|
4617
4788
|
});
|
|
4618
4789
|
try {
|
|
4619
4790
|
broker.control.beginPhaseRun({
|
|
@@ -4788,7 +4959,7 @@ function getRecoveryState(db, collabId2) {
|
|
|
4788
4959
|
}
|
|
4789
4960
|
|
|
4790
4961
|
// src/runtime/broker-daemon.ts
|
|
4791
|
-
import { existsSync as
|
|
4962
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
4792
4963
|
import { spawn } from "node:child_process";
|
|
4793
4964
|
import { dirname as dirname2, resolve as resolve2 } from "node:path";
|
|
4794
4965
|
import { fileURLToPath } from "node:url";
|