ai-whisper 0.6.1 → 0.8.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 CHANGED
@@ -99,7 +99,7 @@ whisper collab mount claude
99
99
  whisper collab mount codex
100
100
  ```
101
101
 
102
- The first `mount` creates the collab and starts the broker daemon for the workspace; the second binds the other agent. From either session, start a workflow against a spec or goal file (`spec-driven-development` for a spec, `ralph-loop` for an open-ended goal). Watch it run with:
102
+ The first `mount` creates the collab and starts the broker daemon for the workspace; the second binds the other agent. From either session, start a workflow against a spec or goal file `spec-driven-development` for a spec, `ralph-loop` for an open-ended goal, plus `complex-bug-fixing` and `deliberation` (see [Workflows](docs/workflows.md)). Watch it run with:
103
103
 
104
104
  ```bash
105
105
  whisper collab dashboard
@@ -121,7 +121,7 @@ For the full mental model, read [Concepts](docs/concepts.md).
121
121
 
122
122
  ## Learn more
123
123
 
124
- - [Workflows](docs/workflows.md) — how to use the workflows well: choosing between `spec-driven-development` and `ralph-loop`, and authoring the spec or goal that drives the run.
124
+ - [Workflows](docs/workflows.md) — how to use the four workflows well: choosing between `spec-driven-development`, `ralph-loop`, `complex-bug-fixing`, and `deliberation`, and authoring the spec, goal, bug report, or seed that drives the run.
125
125
  - [Concepts](docs/concepts.md) — the mental model: baton handoff, real mounted sessions, supervised autonomy, workflow-first execution.
126
126
  - [Relay & handoff flows](docs/relay-handoff-flows.md) — the complete handoff state machine, capture-status table, hotkey reference, per-step verdicts, and troubleshooting.
127
127
  - [Evaluator configuration](docs/evaluator-configuration.md) — required credentials and options for the LLM evaluator that gates workflows.
@@ -1998,6 +1998,7 @@ function buildCollabSummary(db, collabId2) {
1998
1998
  return {
1999
1999
  collabId: e.collabId,
2000
2000
  label,
2001
+ workspaceRoot: collab2?.workspaceRoot ?? "",
2001
2002
  workflowId: wf?.workflowId ?? null,
2002
2003
  workflowType: wf?.workflowType ?? null,
2003
2004
  workflowStatus: wf?.status ?? null,
@@ -2211,11 +2212,51 @@ Findings: (omit this block entirely if none)
2211
2212
 
2212
2213
  <verdict line: "Approved. <one or two sentences>" OR, when blocked/cannot-proceed, state you cannot proceed and why>
2213
2214
 
2215
+ Non-blocking risks:
2216
+ - <risk that does NOT block this gate, or "None.">
2217
+ --- end protocol ---`;
2218
+ var WORKFLOW_DELIBERATION_PROTOCOL = `--- ai-whisper deliberation review protocol ---
2219
+ You are the Challenger at an ai-whisper Deliberation layer gate. No human is in the loop. The Explorer has proposed this layer's output; your job is to produce GENUINE adversarial pressure, not to rubber-stamp. The gate stays shut until the layer's output survives real attack \u2014 OR the only remaining material questions are preference-dependent forks, which you RECORD rather than resolve.
2220
+
2221
+ Decision-materiality is the bar. Sort every finding four ways:
2222
+ - BLOCKING (drives not-approved): it would change a reasonable decision-maker's choice.
2223
+ - OPEN QUESTION (non-blocking): material but depends on the human's private preference \u2014 surface it, do not resolve it, do not block on it.
2224
+ - NON-BLOCKING RISK (non-blocking): a real but non-decision-changing risk \u2014 surface it under the Non-blocking risks section (always last), never gag it.
2225
+ - SUPPRESS: pure noise (style/taste) only.
2226
+
2227
+ Required procedure:
2228
+ 1. Independent derivation (skin in the game): on this layer's FIRST review, derive your OWN candidate (your own objectives / approach set / tradeoff map) BEFORE reading the Explorer's, then diff. Report the material gap. On fix-rounds, re-audit the revision.
2229
+ 2. Forbid recall-based verification: you may NOT clear a material claim with "that matches what I know" \u2014 you and the Explorer share priors and therefore blind spots. Verify every material claim against an EXTERNAL source (re-run the grep, open the cited file, fetch the source, check the actual API) and tag it "verified against <source>". A material claim you cannot externally verify becomes an Open Question marked "could not verify \u2014 you should".
2230
+ 3. Verify by materiality and surface, not by felt uncertainty: verify what is load-bearing and what is a specific or suspiciously-convenient fact (version numbers, API signatures, file paths, citations, benchmarks), regardless of how confident it sounds.
2231
+ 4. Steelman, then attack, with coverage: state the strongest version of the Explorer's position, then surface at least one unexamined assumption, one missing alternative, and one material risk \u2014 or explicitly certify that each lens was run and why none is decision-material. "Looks good" is not a legal handback.
2232
+ 5. Generative mandate: beyond auditing the Explorer, independently GENERATE angles and alternatives it did not raise.
2233
+ 6. Distrust your own agreement: treat your own "yeah, that's right" as a red flag to verify, not a green light.
2234
+
2235
+ Per-layer attack weighting: objectives -> framing + assumption (is the QUESTION itself right?); approaches -> alternative + evidence (is the set complete, is each grounded and feasible?); tradeoffs -> feasibility + second-order (honest, or cherry-picked / buried costs?); synthesis -> faithfulness (does the findings doc match the deliberation; are the Open Questions the right ones?).
2236
+
2237
+ You emit approved / not-approved + findings ONLY. You do NOT label the workflow outcome (advance / loop / escalate) \u2014 whether the gate result causes the run to advance, loop, or escalate is the evaluator's job. Approve only when the layer survived real attack with the work shown. If a required input is missing and the Explorer cannot supply it, do NOT approve \u2014 name the missing input and state you cannot proceed (so the gate escalates). If exploring this layer reveals that an EARLIER ratified layer is wrong, state you cannot proceed and why (the run escalates; it does not silently re-open the earlier layer).
2238
+
2239
+ Never reply with only a bare verdict; your full reply must be well over 100 characters.
2240
+
2241
+ Output format \u2014 the verdict line MUST come before the Non-blocking risks section, which is always LAST:
2242
+ Deliberation review matrix:
2243
+ | Claim / candidate | My independent derivation | Material gap | Verified against | Result |
2244
+ | ... |
2245
+
2246
+ Findings: (omit this block entirely if none)
2247
+ - <blocking finding tied to a decision-material gap, with external-verification evidence>
2248
+
2249
+ Open Questions: (omit if none)
2250
+ - <material but preference-dependent fork, or "could not verify X \u2014 you should">
2251
+
2252
+ <verdict line: "Approved. <one or two sentences>" OR, when blocked/cannot-proceed, state you cannot proceed and why>
2253
+
2214
2254
  Non-blocking risks:
2215
2255
  - <risk that does NOT block this gate, or "None.">
2216
2256
  --- end protocol ---`;
2217
2257
  var CODE_REVIEW_SKILL_GUIDANCE = "Use the ai-whisper-code-review skill to evaluate the delivered code. The workflow review protocol below controls your output format and evaluator semantics; the skill controls how you inspect code and decide which code-quality issues are blocking.\n\n";
2218
2258
  var PLAN_EXECUTION_SKILL_GUIDANCE = "Use the ai-whisper-plan-execution skill to structure HOW you execute this plan: subagent fan-out with model allocation where your harness supports it, disciplined inline execution otherwise. The handback contract above remains authoritative \u2014 settle all delegated work before handing back, and state which execution mode you used.";
2259
+ var DELIBERATION_CRAFT_SKILL_GUIDANCE = "Use the ai-whisper-deliberation-craft skill for HOW to do the research and the attack \u2014 the Explorer's research contract and the Challenger's attack taxonomy. The deliberation review protocol below remains authoritative for the gate contract and output format; the craft skill is how-to only and does not restate it.\n\n";
2219
2260
  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;
2220
2261
  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" + CODE_REVIEW_SKILL_GUIDANCE + WORKFLOW_REVIEW_PROTOCOL;
2221
2262
  var SDD_PLAN_EXECUTION = "Execute the plan at {planPath} now: make all changes it specifies, run the verification command the plan or spec defines and ensure it passes, then commit. This is an autonomous workflow \u2014 no human will respond. Do the work yourself; never ask for confirmation, permission, or clarification. Hand back the commit SHAs and the verification output, plus 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.\n\n" + PLAN_EXECUTION_SKILL_GUIDANCE;
@@ -2425,6 +2466,129 @@ var COMPLEX_BUG_FIXING = {
2425
2466
  }
2426
2467
  ]
2427
2468
  };
2469
+ var DELIB_REVIEW = "You are the Challenger reviewing the Explorer's output for the current Deliberation layer, grounded in the seed at {specPath}. The Explorer's working notes are under {deliberationDir}. This is an autonomous workflow with no human in the loop.\n\n" + DELIBERATION_CRAFT_SKILL_GUIDANCE + WORKFLOW_DELIBERATION_PROTOCOL;
2470
+ var DELIB_SYNTHESIS_REVIEW = "You are the Challenger reviewing the Explorer's SYNTHESIS layer \u2014 the committed findings document at {findingsPath}. Read {findingsPath}: it is the deliverable under review. It must faithfully collapse the ratified objectives/approaches/tradeoffs (the working notes under {deliberationDir}) into the findings skeleton \u2014 verify that faithfulness and that the Open Questions are the right ones (no material decision silently resolved, none invented). Grounded in the seed at {specPath}. This is an autonomous workflow with no human in the loop.\n\n" + DELIBERATION_CRAFT_SKILL_GUIDANCE + WORKFLOW_DELIBERATION_PROTOCOL;
2471
+ var DELIB_FIX = `Apply the Challenger's findings to your working notes under {deliberationDir} now, re-grounding against the seed at {specPath}. This is an autonomous workflow \u2014 no human will respond. Do the research/edits yourself; never ask for confirmation, permission, or clarification. Append one JSON line to {deliberationDir}/metrics.jsonl with the two signals spec \xA712.6 requires \u2014 "materialFindings" (the count of decision-material findings you are addressing this round) and "revisionMagnitude" (how much your output changed: "none"/"minor"/"moderate"/"major") \u2014 plus "layer" and "round". 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.
2472
+
2473
+ ` + DELIBERATION_CRAFT_SKILL_GUIDANCE;
2474
+ var DELIB_OBJECTIVES = `You are the Explorer in an autonomous Deliberation workflow \u2014 no human is in the loop; never ask for confirmation, permission, or clarification, and do the work yourself.
2475
+
2476
+ LAYER 1 of 4: OBJECTIVES. Read the seed at {specPath}. Research the REAL situation it points at \u2014 current state, the actual pain, the constraints. Ground every claim in the project (code, tests, git history, docs) where the seed is project-anchored; use web research only where the question reaches beyond the project. Do NOT restate the seed \u2014 DERIVE the objectives and success criteria from evidence.
2477
+
2478
+ Write your objectives working notes to {deliberationDir}/objectives.md: each derived objective with why-it-matters and its grounding source; the interpretation you took (and any you discarded) when the seed was vague; and an explicit list of known-unknowns. Append one JSON line to {deliberationDir}/metrics.jsonl with the two signals spec \xA712.6 requires \u2014 "materialFindings" (0 on this initial proposal) and "revisionMagnitude" ("initial" on the first proposal) \u2014 plus "layer" and "round". Example: {"layer":"objectives","round":1,"materialFindings":0,"revisionMagnitude":"initial"}.
2479
+
2480
+ ${DELIBERATION_CRAFT_SKILL_GUIDANCE}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.`;
2481
+ var DELIB_APPROACHES = `You are the Explorer in an autonomous Deliberation workflow \u2014 no human is in the loop; do the work yourself.
2482
+
2483
+ LAYER 2 of 4: APPROACHES. The objectives in {deliberationDir}/objectives.md are ratified \u2014 treat them as ground truth. Research and propose AT LEAST THREE genuinely distinct approaches to reach them. Survey the space first (prior art + in-repo idioms + candidate techniques) before evaluating any candidate; ground each approach in a real precedent. You MAY NOT pick a winner in this layer \u2014 that is the guard against premature convergence.
2484
+
2485
+ Write your approaches working notes to {deliberationDir}/approaches.md: one section per approach (gist, grounding/precedent, why it is distinct), plus the known-unknowns each carries. Append one JSON line to {deliberationDir}/metrics.jsonl with "materialFindings" and "revisionMagnitude" (plus "layer" and "round"), the two per-round signals spec \xA712.6 requires.
2486
+
2487
+ ${DELIBERATION_CRAFT_SKILL_GUIDANCE}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.`;
2488
+ var DELIB_TRADEOFFS = `You are the Explorer in an autonomous Deliberation workflow \u2014 no human is in the loop; do the work yourself.
2489
+
2490
+ LAYER 3 of 4: TRADEOFFS & DIFFICULTY. The approaches in {deliberationDir}/approaches.md are ratified. For EACH surviving approach, map the tradeoffs, difficulty, blast radius, and material risks \u2014 stress-tested against reality (feasibility checks and blast radius in the ACTUAL codebase, named risks, not hand-waving). Do not bury costs or cherry-pick.
2491
+
2492
+ Write your tradeoffs working notes to {deliberationDir}/tradeoffs.md: a per-approach tradeoff/difficulty/risk breakdown, with the grounding for each claim. Append one JSON line to {deliberationDir}/metrics.jsonl with "materialFindings" and "revisionMagnitude" (plus "layer" and "round"), the two per-round signals spec \xA712.6 requires.
2493
+
2494
+ ${DELIBERATION_CRAFT_SKILL_GUIDANCE}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.`;
2495
+ var DELIB_SYNTHESIS_FIX = 'Apply the Challenger\'s findings by RE-WRITING the corrected findings document to {findingsPath} (keep the \xA79 skeleton). Then RE-COMMIT it: `git add {findingsPath} && git commit` (do NOT commit {deliberationDir}; it is gitignored). Re-ground against the seed at {specPath} to confirm the revision is consistent. Append one JSON line to {deliberationDir}/metrics.jsonl with the two signals spec \xA712.6 requires \u2014 "materialFindings" (the count of decision-material findings you are addressing this round) and "revisionMagnitude" (how much your output changed: "none"/"minor"/"moderate"/"major") \u2014 plus "layer" and "round". 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.\n\n' + DELIBERATION_CRAFT_SKILL_GUIDANCE;
2496
+ var DELIB_SYNTHESIS = `You are the Explorer in an autonomous Deliberation workflow \u2014 no human is in the loop; do the work yourself.
2497
+
2498
+ LAYER 4 of 4: SYNTHESIS. The objectives, approaches, and tradeoffs working notes under {deliberationDir} are all ratified. Collapse them into a single findings document optimized for fast human review, and WRITE it to {findingsPath} (create the directory if needed). Use exactly this skeleton:
2499
+
2500
+ # Deliberation: <topic>
2501
+ seed: {specPath} \xB7 <date> \xB7 <implementer>/<challenger>
2502
+
2503
+ ## TL;DR
2504
+ - Recommended direction: <one line> (PROPOSAL)
2505
+ - Decisions that need you: <the 1-2 top Open Questions>
2506
+
2507
+ ## Objectives (ratified)
2508
+ - <objective> \u2014 why it matters [source]
2509
+ - interpretation taken: ...; discarded: ... (only when the seed was vague)
2510
+
2511
+ ## Approaches considered
2512
+ | Approach | Gist | Grounding/precedent | Key tradeoff | Difficulty |
2513
+ (one short paragraph per surviving approach only)
2514
+
2515
+ ## Recommendation (PROPOSAL \u2014 overridable)
2516
+ <which, why over the others, and what the Challenger attacked + how it held>
2517
+
2518
+ ## Risks
2519
+ - <material risk to the recommended direction>
2520
+
2521
+ ## Open Questions (for you)
2522
+ - <decision> \u2014 what is at stake / why it was not settled
2523
+ - <"could not verify X \u2014 you should">
2524
+
2525
+ ## Grounding & confidence (footer)
2526
+ verified against source: ... \xB7 assumed (unverified): ... \xB7 couldn't verify: ...
2527
+
2528
+ The recommendation is a clearly-marked PROPOSAL the human may override \u2014 do NOT present it as settled. Then COMMIT the findings doc: \`git add {findingsPath} && git commit\` (do NOT commit {deliberationDir}; it is gitignored). Append one JSON line to {deliberationDir}/metrics.jsonl with "materialFindings" and "revisionMagnitude" (plus "layer" and "round"), the two per-round signals spec \xA712.6 requires.
2529
+
2530
+ ${DELIBERATION_CRAFT_SKILL_GUIDANCE}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.`;
2531
+ var DELIBERATION = {
2532
+ type: "deliberation",
2533
+ displayName: "Deliberation",
2534
+ description: "Paired-agent pre-spec exploration: the Explorer proposes and the Challenger adversarially stress-tests across Objectives -> Approaches -> Tradeoffs -> Synthesis, producing a findings doc for human review",
2535
+ defaultImplementer: "claude",
2536
+ defaultReviewer: "codex",
2537
+ phases: [
2538
+ {
2539
+ name: "objectives",
2540
+ implementerRole: "implementer",
2541
+ reviewerRole: "reviewer",
2542
+ maxRounds: 6,
2543
+ initialHandoffStep: "implement",
2544
+ kickoffTemplate: DELIB_OBJECTIVES,
2545
+ stepTemplates: { implement: DELIB_OBJECTIVES, review: DELIB_REVIEW, fix: DELIB_FIX },
2546
+ reviewMode: "phase-review",
2547
+ evaluatorPromptKey: "deliberation-loop",
2548
+ artifactOut: { kind: "spec", pathTemplate: "{deliberationDir}/objectives.md" },
2549
+ renderFixTemplateOnFindings: true
2550
+ },
2551
+ {
2552
+ name: "approaches",
2553
+ implementerRole: "implementer",
2554
+ reviewerRole: "reviewer",
2555
+ maxRounds: 10,
2556
+ initialHandoffStep: "implement",
2557
+ kickoffTemplate: DELIB_APPROACHES,
2558
+ stepTemplates: { implement: DELIB_APPROACHES, review: DELIB_REVIEW, fix: DELIB_FIX },
2559
+ reviewMode: "phase-review",
2560
+ evaluatorPromptKey: "deliberation-loop",
2561
+ artifactOut: { kind: "spec", pathTemplate: "{deliberationDir}/approaches.md" },
2562
+ renderFixTemplateOnFindings: true
2563
+ },
2564
+ {
2565
+ name: "tradeoffs",
2566
+ implementerRole: "implementer",
2567
+ reviewerRole: "reviewer",
2568
+ maxRounds: 10,
2569
+ initialHandoffStep: "implement",
2570
+ kickoffTemplate: DELIB_TRADEOFFS,
2571
+ stepTemplates: { implement: DELIB_TRADEOFFS, review: DELIB_REVIEW, fix: DELIB_FIX },
2572
+ reviewMode: "phase-review",
2573
+ evaluatorPromptKey: "deliberation-loop",
2574
+ artifactOut: { kind: "spec", pathTemplate: "{deliberationDir}/tradeoffs.md" },
2575
+ renderFixTemplateOnFindings: true
2576
+ },
2577
+ {
2578
+ name: "synthesis",
2579
+ implementerRole: "implementer",
2580
+ reviewerRole: "reviewer",
2581
+ maxRounds: 5,
2582
+ initialHandoffStep: "implement",
2583
+ kickoffTemplate: DELIB_SYNTHESIS,
2584
+ stepTemplates: { implement: DELIB_SYNTHESIS, review: DELIB_SYNTHESIS_REVIEW, fix: DELIB_SYNTHESIS_FIX },
2585
+ reviewMode: "phase-review",
2586
+ evaluatorPromptKey: "deliberation-loop",
2587
+ artifactOut: { kind: "spec", pathTemplate: "{findingsPath}" },
2588
+ renderFixTemplateOnFindings: true
2589
+ }
2590
+ ]
2591
+ };
2428
2592
  function withOperatorControl(def) {
2429
2593
  return {
2430
2594
  ...def,
@@ -2439,7 +2603,8 @@ ${WORKFLOW_OPERATOR_CONTROL}`
2439
2603
  var REGISTRY = {
2440
2604
  [SPEC_DRIVEN_DEVELOPMENT.type]: withOperatorControl(SPEC_DRIVEN_DEVELOPMENT),
2441
2605
  [RALPH_LOOP.type]: withOperatorControl(RALPH_LOOP),
2442
- [COMPLEX_BUG_FIXING.type]: withOperatorControl(COMPLEX_BUG_FIXING)
2606
+ [COMPLEX_BUG_FIXING.type]: withOperatorControl(COMPLEX_BUG_FIXING),
2607
+ [DELIBERATION.type]: withOperatorControl(DELIBERATION)
2443
2608
  };
2444
2609
  function ralphRunDir(workspaceRoot, workflowId) {
2445
2610
  return join(workspaceRoot, ".ai-whisper", "ralph", workflowId);
@@ -2455,6 +2620,19 @@ function bugfixPaths(workspaceRoot, workflowId) {
2455
2620
  postmortemPath: join(bugfixDir, "postmortem.md")
2456
2621
  };
2457
2622
  }
2623
+ function deliberationRunDir(workspaceRoot, workflowId) {
2624
+ return join(workspaceRoot, ".ai-whisper", "deliberation", workflowId);
2625
+ }
2626
+ function deriveFindingsPath(specPath, dateIso) {
2627
+ if (!/^\d{4}-\d{2}-\d{2}/.test(dateIso)) {
2628
+ throw new Error(`deriveFindingsPath: dateIso must start with YYYY-MM-DD, got "${dateIso}"`);
2629
+ }
2630
+ const basename3 = specPath.split("/").pop() ?? "";
2631
+ const withoutExt = basename3.replace(/\.[^.]+$/, "");
2632
+ const slug = withoutExt.replace(/^\d{4}-\d{2}-\d{2}-/, "").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || "deliberation";
2633
+ const date = dateIso.slice(0, 10);
2634
+ return `docs/superpowers/deliberations/${date}-${slug}.md`;
2635
+ }
2458
2636
  function getWorkflowDefinition(type) {
2459
2637
  return REGISTRY[type];
2460
2638
  }
@@ -2514,6 +2692,13 @@ function safeDerivePlanPath(specPath, createdAt) {
2514
2692
  return `${dir}${stem}.plan.md`;
2515
2693
  }
2516
2694
  }
2695
+ function safeDeriveFindingsPath(specPath, createdAt) {
2696
+ try {
2697
+ return deriveFindingsPath(specPath, createdAt);
2698
+ } catch {
2699
+ return specPath;
2700
+ }
2701
+ }
2517
2702
  function composeResumeNotice(input) {
2518
2703
  const hasFiles = input.changedFiles.length > 0;
2519
2704
  const hasMessage = !!input.message && input.message.trim().length > 0;
@@ -2724,7 +2909,9 @@ function createWorkflowControl(deps) {
2724
2909
  reviewMode,
2725
2910
  bugfixDir: bf.bugfixDir,
2726
2911
  diagnosisPath: bf.diagnosisPath,
2727
- postmortemPath: bf.postmortemPath
2912
+ postmortemPath: bf.postmortemPath,
2913
+ deliberationDir: collab2 ? deliberationRunDir(collab2.workspaceRoot, input.workflow.workflowId) : "",
2914
+ findingsPath: safeDeriveFindingsPath(input.workflow.specPath, input.workflow.createdAt)
2728
2915
  });
2729
2916
  }
2730
2917
  function consumeResumeNotice(input) {
@@ -2790,7 +2977,9 @@ ${requestText}` : requestText;
2790
2977
  reviewMode: phase.reviewMode ?? "phase-review",
2791
2978
  bugfixDir: bf.bugfixDir,
2792
2979
  diagnosisPath: bf.diagnosisPath,
2793
- postmortemPath: bf.postmortemPath
2980
+ postmortemPath: bf.postmortemPath,
2981
+ deliberationDir: collab2 ? deliberationRunDir(collab2.workspaceRoot, input.workflow.workflowId) : "",
2982
+ findingsPath: safeDeriveFindingsPath(input.workflow.specPath, input.workflow.createdAt)
2794
2983
  });
2795
2984
  const chainId = `relay_ch_${randomUUID().replace(/-/g, "").slice(0, 16)}`;
2796
2985
  const phaseRunId = `wfp_${randomUUID().replace(/-/g, "").slice(0, 16)}`;
@@ -3062,7 +3251,9 @@ ${requestText}` : requestText;
3062
3251
  ralphDir,
3063
3252
  bugfixDir: bf.bugfixDir,
3064
3253
  diagnosisPath: bf.diagnosisPath,
3065
- postmortemPath: bf.postmortemPath
3254
+ postmortemPath: bf.postmortemPath,
3255
+ deliberationDir: collab2 ? deliberationRunDir(collab2.workspaceRoot, workflow.workflowId) : "",
3256
+ findingsPath: safeDeriveFindingsPath(workflow.specPath, workflow.createdAt)
3066
3257
  });
3067
3258
  fixRequestText = `${fixTmpl}
3068
3259
 
@@ -5255,6 +5446,23 @@ function ensureBugfixWorkspace(workspaceRoot, workflowId) {
5255
5446
  return dir;
5256
5447
  }
5257
5448
 
5449
+ // ../broker/dist/runtime/deliberation-setup.js
5450
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, existsSync as existsSync4 } from "node:fs";
5451
+ import { join as join5 } from "node:path";
5452
+ function ensureDeliberationWorkspace(workspaceRoot, workflowId) {
5453
+ const dir = deliberationRunDir(workspaceRoot, workflowId);
5454
+ mkdirSync3(dir, { recursive: true });
5455
+ const ignorePath = join5(workspaceRoot, ".ai-whisper", ".gitignore");
5456
+ if (!existsSync4(ignorePath)) {
5457
+ writeFileSync3(ignorePath, "*\n");
5458
+ }
5459
+ const deliberationIgnorePath = join5(workspaceRoot, ".ai-whisper", "deliberation", ".gitignore");
5460
+ if (!existsSync4(deliberationIgnorePath)) {
5461
+ writeFileSync3(deliberationIgnorePath, "*\n");
5462
+ }
5463
+ return dir;
5464
+ }
5465
+
5258
5466
  // ../broker/dist/runtime/workflow-driver.js
5259
5467
  function createWorkflowDriver(deps) {
5260
5468
  const { broker: broker2, headReader } = deps;
@@ -5357,6 +5565,20 @@ function createWorkflowDriver(deps) {
5357
5565
  }
5358
5566
  }
5359
5567
  const bugfix = bugfixPaths(collab2.workspaceRoot, workflowId);
5568
+ if (workflow.workflowType === "deliberation") {
5569
+ try {
5570
+ ensureDeliberationWorkspace(collab2.workspaceRoot, workflowId);
5571
+ } catch (err) {
5572
+ broker2.control.haltWorkflow({ workflowId, reason: `deliberation setup failed: ${String(err)}`, now });
5573
+ return;
5574
+ }
5575
+ }
5576
+ const deliberationDir = deliberationRunDir(collab2.workspaceRoot, workflowId);
5577
+ let findingsPath = workflow.specPath;
5578
+ try {
5579
+ findingsPath = deriveFindingsPath(workflow.specPath, workflow.createdAt);
5580
+ } catch {
5581
+ }
5360
5582
  const ctx = workflow.workflowContext;
5361
5583
  let planPath = workflow.specPath;
5362
5584
  try {
@@ -5371,7 +5593,9 @@ function createWorkflowDriver(deps) {
5371
5593
  reviewMode: phase.reviewMode ?? "phase-review",
5372
5594
  bugfixDir: bugfix.bugfixDir,
5373
5595
  diagnosisPath: bugfix.diagnosisPath,
5374
- postmortemPath: bugfix.postmortemPath
5596
+ postmortemPath: bugfix.postmortemPath,
5597
+ deliberationDir,
5598
+ findingsPath
5375
5599
  });
5376
5600
  try {
5377
5601
  broker2.control.beginPhaseRun({
@@ -5536,7 +5760,7 @@ var EVENT_PROTOCOL_VERSION = "1";
5536
5760
 
5537
5761
  // ../broker/dist/runtime/event-socket-server.js
5538
5762
  import { createServer } from "node:net";
5539
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2 } from "node:fs";
5763
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, unlinkSync as unlinkSync2 } from "node:fs";
5540
5764
  import { dirname as dirname2 } from "node:path";
5541
5765
  var EVENT_NAME_TABLE = {
5542
5766
  "chain.resolved": true,
@@ -5555,8 +5779,8 @@ var ALL_BROKER_EVENT_NAMES = Object.keys(EVENT_NAME_TABLE);
5555
5779
  async function createEventSocketServer(input) {
5556
5780
  const now = input.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
5557
5781
  const clients = /* @__PURE__ */ new Set();
5558
- mkdirSync3(dirname2(input.socketPath), { recursive: true });
5559
- if (existsSync4(input.socketPath)) {
5782
+ mkdirSync4(dirname2(input.socketPath), { recursive: true });
5783
+ if (existsSync5(input.socketPath)) {
5560
5784
  try {
5561
5785
  unlinkSync2(input.socketPath);
5562
5786
  } catch {
@@ -5612,7 +5836,7 @@ async function createEventSocketServer(input) {
5612
5836
  dropClient(conn);
5613
5837
  server.close(() => {
5614
5838
  try {
5615
- if (existsSync4(input.socketPath))
5839
+ if (existsSync5(input.socketPath))
5616
5840
  unlinkSync2(input.socketPath);
5617
5841
  } catch {
5618
5842
  }
@@ -5750,6 +5974,24 @@ var REVIEW_JSON_SCHEMA = {
5750
5974
  },
5751
5975
  required: ["verdict", "confidence", "reason"]
5752
5976
  };
5977
+ var DELIBERATION_REVIEW_SYSTEM_PROMPT = `You are a neutral judge evaluating a Challenger's verdict on ONE layer of an autonomous Deliberation workflow. The Challenger adversarially stress-tests an Explorer's reasoning. Your job is NOT to re-derive the layer yourself \u2014 it is to classify the Challenger's handback into a verdict AND to reject a HOLLOW approval that did not actually do the adversarial work.
5978
+
5979
+ Input is a JSON object including handbackText (the Challenger's response) and contextual fields.
5980
+
5981
+ Respond with a JSON object \u2014 classification ONLY (do NOT reproduce findings; the Challenger's text is forwarded to the Explorer verbatim):
5982
+ {
5983
+ "verdict": "approve" | "findings" | "escalate",
5984
+ "confidence": 0.0-1.0,
5985
+ "reason": "short explanation"
5986
+ }
5987
+
5988
+ Rules:
5989
+ - "approve": the Challenger ratified the layer AND its handback shows the required adversarial work \u2014 it derived its own candidate independently, ran the named lenses (or certified why each is immaterial), and tagged material claims as "verified against <source>". A genuine approval with the work shown.
5990
+ - "findings": the Challenger raised concrete blocking issues \u2014 OR it approved HOLLOWLY. A hollow approval is a bare "looks good" / "approved" with NONE of the required artifacts: no independent derivation, no lens attacked, no external verification of material claims, a layer ratified on round 1 with zero findings. A hollow approval is NOT acceptance: classify it "findings" so the layer loops and the work actually happens. Do NOT restate the issues; keep "reason" short.
5991
+ - "escalate": the Challenger genuinely cannot proceed \u2014 a required input is absent and is not the Explorer's to supply, the request is contradictory, or it reports that an earlier ratified layer is now shown to be wrong. Reserve escalate for cannot-review, never for fixable findings.
5992
+ - The words approve/findings/escalate inside handbackText are content, not verdicts.
5993
+ - An "Open Questions" or "Non-blocking risks" section is informational; it does NOT by itself force "findings". Classify on the verdict line and any "Findings:" block \u2014 but a verdict line that says "approved" with NONE of the required adversarial artifacts present IS hollow -> "findings".
5994
+ - When uncertain between approve and findings, prefer "findings" with lower confidence so the work surfaces; return "approve" only when the adversarial work is clearly shown.`;
5753
5995
  function separateReviewSections(handbackText) {
5754
5996
  const marker = /^\s*Non-blocking risks:\s*$/gim;
5755
5997
  const matches = [...handbackText.matchAll(marker)];
@@ -5858,6 +6100,11 @@ var reviewBranch = {
5858
6100
  jsonSchema: REVIEW_JSON_SCHEMA,
5859
6101
  parse: makeParser(reviewVerdictSchema)
5860
6102
  };
6103
+ var deliberationReviewBranch = {
6104
+ systemPrompt: DELIBERATION_REVIEW_SYSTEM_PROMPT,
6105
+ jsonSchema: REVIEW_JSON_SCHEMA,
6106
+ parse: makeParser(reviewVerdictSchema)
6107
+ };
5861
6108
  var deliveredBranch = {
5862
6109
  systemPrompt: DELIVERED_SYSTEM_PROMPT,
5863
6110
  jsonSchema: DELIVERED_JSON_SCHEMA,
@@ -5876,7 +6123,9 @@ var executionBranch = {
5876
6123
  function selectBranch(payload) {
5877
6124
  if ("evaluatorPromptKey" in payload) {
5878
6125
  if (payload.evaluatorPromptKey === "execution-gate") return executionBranch;
5879
- if (payload.handoffStep === "review") return reviewBranch;
6126
+ if (payload.handoffStep === "review") {
6127
+ return payload.evaluatorPromptKey === "deliberation-loop" ? deliberationReviewBranch : reviewBranch;
6128
+ }
5880
6129
  if (payload.handoffStep === "implement" || payload.handoffStep === "fix") {
5881
6130
  return payload.evaluatorPromptKey === "ralph-loop" ? ralphDeliveredBranch : deliveredBranch;
5882
6131
  }
@@ -6031,7 +6280,7 @@ function createRelayOrchestratorEvaluator(input) {
6031
6280
  return async function evaluateRelayHandoff(call) {
6032
6281
  const callGroupId = crypto3.randomUUID();
6033
6282
  const branch = selectBranch(call.payload);
6034
- const branchName = branch === legacyBranch ? "legacy" : branch === reviewBranch ? "review" : branch === deliveredBranch || branch === ralphDeliveredBranch ? "delivered" : "execution";
6283
+ const branchName = branch === legacyBranch ? "legacy" : branch === reviewBranch || branch === deliberationReviewBranch ? "review" : branch === deliveredBranch || branch === ralphDeliveredBranch ? "delivered" : "execution";
6035
6284
  const effectivePayload = branch === reviewBranch && "handbackText" in call.payload ? {
6036
6285
  ...call.payload,
6037
6286
  handbackText: separateReviewSections(call.payload.handbackText).body
@@ -6437,7 +6686,7 @@ function writeOwnPidToBrokerDaemon(db, input) {
6437
6686
 
6438
6687
  // src/runtime/evaluator-config.ts
6439
6688
  import { readFileSync as readFileSync2, statSync as statSync3 } from "node:fs";
6440
- import { join as join5 } from "node:path";
6689
+ import { join as join6 } from "node:path";
6441
6690
 
6442
6691
  // src/runtime/state-root.ts
6443
6692
  import os from "node:os";
@@ -6499,15 +6748,15 @@ function loadEvaluatorConfig() {
6499
6748
  const root = getStateRoot();
6500
6749
  let dotenv = {};
6501
6750
  try {
6502
- dotenv = parseDotEnv(readFileSync2(join5(root, ".env"), "utf8"));
6751
+ dotenv = parseDotEnv(readFileSync2(join6(root, ".env"), "utf8"));
6503
6752
  } catch (err) {
6504
6753
  if (err.code !== "ENOENT") throw err;
6505
6754
  }
6506
6755
  const envGet = (k) => process.env[k] ?? dotenv[k];
6507
- const authPath = join5(root, "auth.json");
6756
+ const authPath = join6(root, "auth.json");
6508
6757
  warnIfLoosePerms(authPath);
6509
6758
  const auth = readJsonFile(authPath, "auth.json");
6510
- const config = readJsonFile(join5(root, "config.json"), "config.json");
6759
+ const config = readJsonFile(join6(root, "config.json"), "config.json");
6511
6760
  const evalCfg = config?.evaluator ?? {};
6512
6761
  const evalOllama = evalCfg.ollama ?? {};
6513
6762
  const provider = (envGet("AI_WHISPER_EVALUATOR_PROVIDER") ?? evalCfg.provider) === "ollama" ? "ollama" : "anthropic";
@@ -6554,7 +6803,7 @@ function recordEvaluatorStatus(db, input) {
6554
6803
 
6555
6804
  // src/bin/broker-daemon.ts
6556
6805
  import { execFile as execFile2 } from "node:child_process";
6557
- import { join as join6 } from "node:path";
6806
+ import { join as join7 } from "node:path";
6558
6807
 
6559
6808
  // src/runtime/cli-package-info.ts
6560
6809
  import { readFileSync as readFileSync3 } from "node:fs";
@@ -6604,7 +6853,7 @@ if (collabId) {
6604
6853
  });
6605
6854
  workflowEventBridge.start();
6606
6855
  eventSocket = await createEventSocketServer({
6607
- socketPath: join6(getStateSocketsDir(), `events-${collabId}.sock`),
6856
+ socketPath: join7(getStateSocketsDir(), `events-${collabId}.sock`),
6608
6857
  events: [broker.events, externalEvents],
6609
6858
  engineVersion: resolveCliVersion()
6610
6859
  });