opencode-swarm 7.17.1 → 7.17.3

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/index.js CHANGED
@@ -33,7 +33,7 @@ var package_default;
33
33
  var init_package = __esm(() => {
34
34
  package_default = {
35
35
  name: "opencode-swarm",
36
- version: "7.17.1",
36
+ version: "7.17.3",
37
37
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
38
38
  main: "dist/index.js",
39
39
  types: "dist/index.d.ts",
@@ -60218,7 +60218,7 @@ async function handleTurboCommand(directory, args2, sessionID) {
60218
60218
  if (arg0 === "on") {
60219
60219
  let strategy = "standard";
60220
60220
  try {
60221
- const { config: config3 } = loadPluginConfigWithMeta(directory);
60221
+ const { config: config3 } = _internals31.loadPluginConfigWithMeta(directory);
60222
60222
  if (config3.turbo?.strategy === "lean") {
60223
60223
  strategy = "lean";
60224
60224
  }
@@ -60273,7 +60273,7 @@ function enableLeanTurbo(session, directory, sessionID) {
60273
60273
  let maxParallelCoders = 4;
60274
60274
  let conflictPolicy = "serialize";
60275
60275
  try {
60276
- const { config: config3 } = loadPluginConfigWithMeta(directory);
60276
+ const { config: config3 } = _internals31.loadPluginConfigWithMeta(directory);
60277
60277
  const leanConfig = config3.turbo?.lean;
60278
60278
  if (leanConfig) {
60279
60279
  maxParallelCoders = leanConfig.max_parallel_coders ?? 4;
@@ -60343,11 +60343,15 @@ function buildStatusMessage(session, directory, sessionID) {
60343
60343
  ].join(`
60344
60344
  `);
60345
60345
  }
60346
+ var _internals31;
60346
60347
  var init_turbo = __esm(() => {
60347
60348
  init_config();
60348
60349
  init_state();
60349
60350
  init_state3();
60350
60351
  init_logger();
60352
+ _internals31 = {
60353
+ loadPluginConfigWithMeta
60354
+ };
60351
60355
  });
60352
60356
 
60353
60357
  // src/commands/write-retro.ts
@@ -60600,7 +60604,7 @@ function createSwarmCommandHandler(directory, agents) {
60600
60604
  const attemptedCommand = tokens[0] || "";
60601
60605
  const MAX_DISPLAY = 100;
60602
60606
  const displayCommand = attemptedCommand.length > MAX_DISPLAY ? `${attemptedCommand.slice(0, MAX_DISPLAY)}...` : attemptedCommand;
60603
- const similar = _internals31.findSimilarCommands(attemptedCommand);
60607
+ const similar = _internals32.findSimilarCommands(attemptedCommand);
60604
60608
  const header = `Command \`/swarm ${displayCommand}\` not found.`;
60605
60609
  const suggestions = similar.length > 0 ? `Did you mean:
60606
60610
  ${similar.map((cmd) => ` • /swarm ${cmd}`).join(`
@@ -60707,7 +60711,7 @@ function findSimilarCommands(query) {
60707
60711
  }
60708
60712
  const scored = VALID_COMMANDS.map((cmd) => {
60709
60713
  const cmdLower = cmd.toLowerCase();
60710
- const fullScore = _internals31.levenshteinDistance(q, cmdLower);
60714
+ const fullScore = _internals32.levenshteinDistance(q, cmdLower);
60711
60715
  let tokenScore = Infinity;
60712
60716
  if (cmd.includes(" ") || cmd.includes("-")) {
60713
60717
  const qTokens = q.split(/[\s-]+/);
@@ -60720,7 +60724,7 @@ function findSimilarCommands(query) {
60720
60724
  for (const ct of cmdTokens) {
60721
60725
  if (ct.length === 0)
60722
60726
  continue;
60723
- const dist = _internals31.levenshteinDistance(qt, ct);
60727
+ const dist = _internals32.levenshteinDistance(qt, ct);
60724
60728
  if (dist < minDist)
60725
60729
  minDist = dist;
60726
60730
  }
@@ -60730,7 +60734,7 @@ function findSimilarCommands(query) {
60730
60734
  }
60731
60735
  const dashStrippedQ = q.replace(/-/g, "");
60732
60736
  const dashStrippedCmd = cmdLower.replace(/-/g, "");
60733
- const dashScore = _internals31.levenshteinDistance(dashStrippedQ, dashStrippedCmd);
60737
+ const dashScore = _internals32.levenshteinDistance(dashStrippedQ, dashStrippedCmd);
60734
60738
  const score = Math.min(fullScore, tokenScore, dashScore);
60735
60739
  return { cmd, score };
60736
60740
  });
@@ -60762,11 +60766,11 @@ async function handleHelpCommand(ctx) {
60762
60766
  return buildHelpText2();
60763
60767
  }
60764
60768
  const tokens = targetCommand.split(/\s+/);
60765
- const resolved = _internals31.resolveCommand(tokens);
60769
+ const resolved = _internals32.resolveCommand(tokens);
60766
60770
  if (resolved) {
60767
- return _internals31.buildDetailedHelp(resolved.key, resolved.entry);
60771
+ return _internals32.buildDetailedHelp(resolved.key, resolved.entry);
60768
60772
  }
60769
- const similar = _internals31.findSimilarCommands(targetCommand);
60773
+ const similar = _internals32.findSimilarCommands(targetCommand);
60770
60774
  const { buildHelpText: fullHelp } = await Promise.resolve().then(() => (init_commands(), exports_commands));
60771
60775
  if (similar.length > 0) {
60772
60776
  return `Command '/swarm ${targetCommand}' not found.
@@ -60860,7 +60864,7 @@ function resolveCommand(tokens) {
60860
60864
  }
60861
60865
  return null;
60862
60866
  }
60863
- var COMMAND_REGISTRY, VALID_COMMANDS, _internals31, validation;
60867
+ var COMMAND_REGISTRY, VALID_COMMANDS, _internals32, validation;
60864
60868
  var init_registry = __esm(() => {
60865
60869
  init_acknowledge_spec_drift();
60866
60870
  init_agents();
@@ -60930,7 +60934,7 @@ var init_registry = __esm(() => {
60930
60934
  clashesWithNativeCcCommand: "/agents"
60931
60935
  },
60932
60936
  help: {
60933
- handler: (ctx) => _internals31.handleHelpCommand(ctx),
60937
+ handler: (ctx) => _internals32.handleHelpCommand(ctx),
60934
60938
  description: "Show help for swarm commands",
60935
60939
  category: "core",
60936
60940
  args: "[command]",
@@ -61229,9 +61233,24 @@ var init_registry = __esm(() => {
61229
61233
  },
61230
61234
  turbo: {
61231
61235
  handler: (ctx) => handleTurboCommand(ctx.directory, ctx.args, ctx.sessionID),
61232
- description: "Toggle Turbo Mode for the active session [on|off]",
61233
- args: "on, off",
61234
- details: 'Toggles Turbo Mode which skips non-critical QA gates for faster iteration. When enabled, the architect can proceed without waiting for all automated checks. Session-scoped — resets on new session. Use "on" or "off" to set explicitly, or toggle with no argument.',
61236
+ description: "Toggle Turbo Mode strategy for the active session [on|off|lean|standard|status]",
61237
+ args: "on, off, lean, standard, status",
61238
+ details: `Toggles Turbo Mode for the current session. Supports two strategies:
61239
+
61240
+ ` + `**Standard turbo** — skips non-critical QA gates for faster iteration.
61241
+ ` + `**Lean turbo** — parallel lane execution with per-lane reviewer gates and file-lock conflict detection.
61242
+ ` + `
61243
+ Subcommands:
61244
+ ` + ` turbo on — enable turbo (uses lean when config turbo.strategy is "lean", otherwise standard)
61245
+ ` + ` turbo off — disable all turbo modes
61246
+ ` + ` turbo lean on — enable Lean Turbo explicitly
61247
+ ` + ` turbo lean off — disable Lean Turbo
61248
+ ` + ` turbo lean — toggle Lean Turbo on/off
61249
+ ` + ` turbo standard on — force standard turbo (disables lean even if config says lean)
61250
+ ` + ` turbo standard off — disable standard turbo (falls back to lean if config strategy is lean)
61251
+ ` + ` turbo status — show detailed status including active strategy and lanes
61252
+ ` + `
61253
+ ` + "Session-scoped — resets on new session.",
61235
61254
  category: "utility"
61236
61255
  },
61237
61256
  "full-auto": {
@@ -61287,7 +61306,7 @@ var init_registry = __esm(() => {
61287
61306
  }
61288
61307
  };
61289
61308
  VALID_COMMANDS = Object.keys(COMMAND_REGISTRY);
61290
- _internals31 = {
61309
+ _internals32 = {
61291
61310
  handleHelpCommand,
61292
61311
  validateAliases,
61293
61312
  resolveCommand,
@@ -61295,7 +61314,7 @@ var init_registry = __esm(() => {
61295
61314
  findSimilarCommands,
61296
61315
  buildDetailedHelp
61297
61316
  };
61298
- validation = _internals31.validateAliases();
61317
+ validation = _internals32.validateAliases();
61299
61318
  if (!validation.valid) {
61300
61319
  throw new Error(`COMMAND_REGISTRY alias validation failed:
61301
61320
  ${validation.errors.join(`
@@ -61439,7 +61458,7 @@ Present the eleven gates with their defaults (DEFAULT_QA_GATES) as a single user
61439
61458
  - mutation_test (default: OFF) — when enabled, runs mutation testing on source files touched this phase via generate_mutants + mutation_test + write_mutation_evidence at PHASE-WRAP; FAIL verdict blocks phase_complete; WARN is non-blocking (recommended for projects with coverage gaps or safety-critical code)
61440
61459
  - council_general_review (default: OFF) — when enabled, MODE: SPECIFY runs convene_general_council on the draft spec before the critic-gate; the architect runs a curated web_search pass, dispatches council_generalist / council_skeptic / council_domain_expert in parallel with a shared RESEARCH CONTEXT block, deliberates on disagreements, and synthesizes the result directly into the spec (recommended for novel architecture, unclear best practices, or high-risk design decisions). Requires council.general.enabled: true and a configured search API key.
61441
61460
  - drift_check (default: ON) — when enabled, mandatory per-phase drift verification via critic_drift_verifier at PHASE-WRAP; compares implemented changes against spec.md intent; hard-blocks phase_complete when spec.md exists and drift evidence is missing or REJECTED; advisory-only when no spec.md exists (recommended for all projects with a specification)
61442
- - final_council (default: OFF) when enabled, after all phases complete the architect convenes a holistic general council review of the entire body of work before project close. Requires council.general.enabled: true in plugin config. Recommended for multi-phase projects with high architectural complexity.
61461
+ - final_council (default: OFF) - when enabled, after all phases complete the architect dispatches the same five phase-council members (\`critic\`, \`reviewer\`, \`sme\`, \`test_engineer\`, \`explorer\`) at project scope, collects \`CouncilMemberVerdict\` objects, and calls \`write_final_council_evidence\`. This is not General Council mode and does not require \`council.general.enabled\`.
61443
61462
 
61444
61463
  One question, one message, defaults pre-stated. Wait for the user's answer.
61445
61464
 
@@ -63279,19 +63298,15 @@ The tool will automatically write the retrospective to \`.swarm/evidence/retro-{
63279
63298
  - \`.swarm/evidence/{phase}/mutation-gate.json\` exists with verdict 'pass' or 'warn' (written by YOU via the \`write_mutation_evidence\` tool after step 5.56) — ONLY required when \`mutation_test\` is enabled in the QA gate profile
63280
63299
  If any required file is missing, run the missing gate first. Turbo mode skips all gates automatically.
63281
63300
  NOTE: Steps 5.5, 5.55, and 5.56 are enforced by runtime hooks. If \`hallucination_guard\` is enabled and you skip the critic_hallucination_verifier delegation (or fail to call \`write_hallucination_evidence\`), phase_complete will be BLOCKED by the plugin. Similarly, if \`mutation_test\` is enabled and you skip step 5.56 (or fail to call \`write_mutation_evidence\`), phase_complete will be BLOCKED. These are not suggestions — they are hard enforcement mechanisms.
63282
- 5.7. **Final Council (conditional on QA gate last phase only)**: Check whether \`final_council\` is enabled in the effective QA gate profile (visible via \`get_qa_gate_profile\`). If disabled, skip silently and proceed to step 6.
63301
+ 5.7. **Final Council (conditional on QA gate - last phase only)**: Check whether \`final_council\` is enabled in the effective QA gate profile (visible via \`get_qa_gate_profile\`). If disabled, skip silently and proceed to step 6.
63283
63302
  If enabled AND this is the LAST phase in the plan (all other phases have status 'complete' and no more phases remain):
63284
- 1. Verify \`council.general.enabled: true\` in plugin config. If not enabled, warn the user: "final_council gate is enabled but General Council is not configured. Skipping final council." Then proceed to step 6. Check that \`convene_general_council\` is available in your tool list. If the tool is unavailable (filtered by config), warn the user and skip.
63285
- 2. Run 1-3 targeted \`web_search\` queries relevant to the project domain.
63286
- 3. Compile a RESEARCH CONTEXT block from search results.
63287
- 4. Dispatch \`{{AGENT_PREFIX}}council_generalist\`, \`{{AGENT_PREFIX}}council_skeptic\`, and \`{{AGENT_PREFIX}}council_domain_expert\` in PARALLEL. Pass: the full body of work (all phase summaries, all evidence artifacts), the RESEARCH CONTEXT block, round number 1. Instruction: "Review the entire body of work holistically. Identify cross-cutting issues, architectural coherence, and overall quality."
63288
- 5. Collect all three JSON responses.
63289
- 6. Call \`convene_general_council\` with mode: 'general', the project summary as question, and the collected round1Responses.
63290
- 7. If disagreements exist, re-dispute as in MODE: COUNCIL step 5-6.
63291
- 8. Present the final synthesis to the user as a project-close summary.
63292
- 9. Write the final council result to \`.swarm/evidence/final-council.json\`.
63293
- 10. Do NOT call \`/swarm close\` until the final council completes (if enabled). The evidence file \`.swarm/evidence/final-council.json\` must exist with an APPROVED verdict before \`/swarm close\` is permitted when final_council is enabled.
63294
- If enabled but NOT the last phase, skip silently — final council only runs once, after all phases.
63303
+ 1. Build a PROJECT DOSSIER from the completed plan, all phase summaries, changed-file summaries, and all relevant evidence artifacts. This is a completed-project review, not General Council mode.
63304
+ 2. Dispatch \`{{AGENT_PREFIX}}critic\`, \`{{AGENT_PREFIX}}reviewer\`, \`{{AGENT_PREFIX}}sme\`, \`{{AGENT_PREFIX}}test_engineer\`, and \`{{AGENT_PREFIX}}explorer\` in PARALLEL with project-scoped context. Each member must review the entire completed body of work and return a \`CouncilMemberVerdict\` JSON object using \`agent\`, \`verdict\` (APPROVE|CONCERNS|REJECT), \`confidence\`, \`findings[]\`, \`criteriaAssessed[]\`, \`criteriaUnmet[]\`, and \`durationMs\`.
63305
+ 3. Collect the five returned verdict objects. Do NOT fabricate, infer, or substitute verdicts. If a member does not return valid JSON, re-dispatch that member.
63306
+ 4. Call \`write_final_council_evidence\` with \`phase\`, \`projectSummary\`, \`roundNumber\`, and the collected \`verdicts\` array. This writes \`.swarm/evidence/final-council.json\` with plan binding, member verdicts, and quorum metadata.
63307
+ 5. Do NOT call \`convene_general_council\`, do NOT dispatch \`council_generalist\`, \`council_skeptic\`, or \`council_domain_expert\`, and do NOT require \`council.general.enabled\` for this gate. \`final_council\` is the same five-member phase council rerun at project scope.
63308
+ 6. Do NOT call \`phase_complete\` or \`/swarm close\` until \`.swarm/evidence/final-council.json\` exists with an approved, plan-bound, quorumed final-council verdict. When \`final_council\` is enabled, \`phase_complete\` will block until that evidence exists.
63309
+ If enabled but NOT the last phase, skip silently - final council only runs once, after all phases.
63295
63310
  6. Summarize to user
63296
63311
  7. Ask: "Ready for Phase [N+1]?"
63297
63312
 
@@ -69912,7 +69927,7 @@ __export(exports_runtime, {
69912
69927
  getSupportedLanguages: () => getSupportedLanguages,
69913
69928
  getInitializedLanguages: () => getInitializedLanguages,
69914
69929
  clearParserCache: () => clearParserCache,
69915
- _internals: () => _internals32
69930
+ _internals: () => _internals33
69916
69931
  });
69917
69932
  import * as path76 from "node:path";
69918
69933
  import { fileURLToPath as fileURLToPath2 } from "node:url";
@@ -69922,10 +69937,10 @@ async function initTreeSitter() {
69922
69937
  const thisDir = path76.dirname(fileURLToPath2(import.meta.url));
69923
69938
  const isSource = thisDir.replace(/\\/g, "/").endsWith("/src/lang");
69924
69939
  if (isSource) {
69925
- await _internals32.parserInit();
69940
+ await _internals33.parserInit();
69926
69941
  } else {
69927
69942
  const grammarsDir = getGrammarsDirAbsolute();
69928
- await _internals32.parserInit({
69943
+ await _internals33.parserInit({
69929
69944
  locateFile(scriptName) {
69930
69945
  return path76.join(grammarsDir, scriptName);
69931
69946
  }
@@ -70027,12 +70042,12 @@ function getInitializedLanguages() {
70027
70042
  function getSupportedLanguages() {
70028
70043
  return Object.keys(LANGUAGE_WASM_MAP);
70029
70044
  }
70030
- var parserCache, initializedLanguages, treeSitterInitPromise = null, _internals32, LANGUAGE_WASM_MAP;
70045
+ var parserCache, initializedLanguages, treeSitterInitPromise = null, _internals33, LANGUAGE_WASM_MAP;
70031
70046
  var init_runtime = __esm(() => {
70032
70047
  init_tree_sitter();
70033
70048
  parserCache = new Map;
70034
70049
  initializedLanguages = new Set;
70035
- _internals32 = {
70050
+ _internals33 = {
70036
70051
  parserInit: Parser.init
70037
70052
  };
70038
70053
  LANGUAGE_WASM_MAP = {
@@ -70489,9 +70504,9 @@ var init_doc_scan = __esm(() => {
70489
70504
  var exports_knowledge_recall = {};
70490
70505
  __export(exports_knowledge_recall, {
70491
70506
  knowledge_recall: () => knowledge_recall,
70492
- _internals: () => _internals33
70507
+ _internals: () => _internals34
70493
70508
  });
70494
- var knowledge_recall, _internals33;
70509
+ var knowledge_recall, _internals34;
70495
70510
  var init_knowledge_recall = __esm(() => {
70496
70511
  init_zod();
70497
70512
  init_knowledge_store();
@@ -70577,7 +70592,7 @@ var init_knowledge_recall = __esm(() => {
70577
70592
  return JSON.stringify(result);
70578
70593
  }
70579
70594
  });
70580
- _internals33 = {
70595
+ _internals34 = {
70581
70596
  knowledge_recall
70582
70597
  };
70583
70598
  });
@@ -70632,7 +70647,7 @@ __export(exports_curator_drift, {
70632
70647
  runDeterministicDriftCheck: () => runDeterministicDriftCheck,
70633
70648
  readPriorDriftReports: () => readPriorDriftReports,
70634
70649
  buildDriftInjectionText: () => buildDriftInjectionText,
70635
- _internals: () => _internals35
70650
+ _internals: () => _internals36
70636
70651
  });
70637
70652
  import * as fs60 from "node:fs";
70638
70653
  import * as path84 from "node:path";
@@ -70677,7 +70692,7 @@ async function runDeterministicDriftCheck(directory, phase, curatorResult, confi
70677
70692
  try {
70678
70693
  const planMd = await readSwarmFileAsync(directory, "plan.md");
70679
70694
  const specMd = await readSwarmFileAsync(directory, "spec.md");
70680
- const priorReports = await _internals35.readPriorDriftReports(directory);
70695
+ const priorReports = await _internals36.readPriorDriftReports(directory);
70681
70696
  const complianceCount = curatorResult.compliance.length;
70682
70697
  const warningCompliance = curatorResult.compliance.filter((obs) => obs.severity === "warning");
70683
70698
  let alignment = "ALIGNED";
@@ -70726,7 +70741,7 @@ async function runDeterministicDriftCheck(directory, phase, curatorResult, confi
70726
70741
  scope_additions: [],
70727
70742
  injection_summary: injectionSummary
70728
70743
  };
70729
- const reportPath = await _internals35.writeDriftReport(directory, report);
70744
+ const reportPath = await _internals36.writeDriftReport(directory, report);
70730
70745
  getGlobalEventBus().publish("curator.drift.completed", {
70731
70746
  phase,
70732
70747
  alignment,
@@ -70789,12 +70804,12 @@ function buildDriftInjectionText(report, maxChars) {
70789
70804
  }
70790
70805
  return text.slice(0, maxChars);
70791
70806
  }
70792
- var DRIFT_REPORT_PREFIX = "drift-report-phase-", _internals35;
70807
+ var DRIFT_REPORT_PREFIX = "drift-report-phase-", _internals36;
70793
70808
  var init_curator_drift = __esm(() => {
70794
70809
  init_event_bus();
70795
70810
  init_logger();
70796
70811
  init_utils2();
70797
- _internals35 = {
70812
+ _internals36 = {
70798
70813
  readPriorDriftReports,
70799
70814
  writeDriftReport,
70800
70815
  runDeterministicDriftCheck,
@@ -70806,7 +70821,7 @@ var init_curator_drift = __esm(() => {
70806
70821
  var exports_project_context = {};
70807
70822
  __export(exports_project_context, {
70808
70823
  buildProjectContext: () => buildProjectContext,
70809
- _internals: () => _internals48,
70824
+ _internals: () => _internals49,
70810
70825
  LANG_BACKEND_DETECTION_TIMEOUT_MS: () => LANG_BACKEND_DETECTION_TIMEOUT_MS
70811
70826
  });
70812
70827
  import * as fs110 from "node:fs";
@@ -70890,7 +70905,7 @@ function selectLintCommand(backend, directory) {
70890
70905
  return null;
70891
70906
  }
70892
70907
  async function buildProjectContext(directory) {
70893
- const backend = await _internals48.pickBackend(directory);
70908
+ const backend = await _internals49.pickBackend(directory);
70894
70909
  if (!backend)
70895
70910
  return null;
70896
70911
  const ctx = emptyProjectContext();
@@ -70921,16 +70936,16 @@ async function buildProjectContext(directory) {
70921
70936
  if (backend.prompts.reviewerChecklist.length > 0) {
70922
70937
  ctx.REVIEWER_CHECKLIST = bulletList(backend.prompts.reviewerChecklist);
70923
70938
  }
70924
- const profiles = _internals48.pickedProfiles(directory);
70939
+ const profiles = _internals49.pickedProfiles(directory);
70925
70940
  if (profiles.length > 1) {
70926
70941
  ctx.PROJECT_CONTEXT_SECONDARY_LANGUAGES = profiles.slice(1).map((p) => p.id).join(", ");
70927
70942
  }
70928
70943
  return ctx;
70929
70944
  }
70930
- var LANG_BACKEND_DETECTION_TIMEOUT_MS = 300, _internals48;
70945
+ var LANG_BACKEND_DETECTION_TIMEOUT_MS = 300, _internals49;
70931
70946
  var init_project_context = __esm(() => {
70932
70947
  init_dispatch();
70933
- _internals48 = {
70948
+ _internals49 = {
70934
70949
  pickBackend,
70935
70950
  pickedProfiles
70936
70951
  };
@@ -81321,10 +81336,10 @@ async function getRunMemorySummary(directory) {
81321
81336
  if (entries.length === 0) {
81322
81337
  return null;
81323
81338
  }
81324
- const groups = _internals34.groupByTaskId(entries);
81339
+ const groups = _internals35.groupByTaskId(entries);
81325
81340
  const summaries = [];
81326
81341
  for (const [taskId, taskEntries] of groups) {
81327
- const summary = _internals34.summarizeTask(taskId, taskEntries);
81342
+ const summary = _internals35.summarizeTask(taskId, taskEntries);
81328
81343
  if (summary) {
81329
81344
  summaries.push(summary);
81330
81345
  }
@@ -81357,7 +81372,7 @@ Use this data to avoid repeating known failure patterns.`;
81357
81372
  }
81358
81373
  return prefix + summaryText + suffix;
81359
81374
  }
81360
- var _internals34 = {
81375
+ var _internals35 = {
81361
81376
  generateTaskFingerprint,
81362
81377
  recordOutcome,
81363
81378
  getTaskHistory,
@@ -85356,6 +85371,99 @@ function buildPhaseCouncilFeedback(phaseNumber, phaseSummary, verdict, vetoedBy,
85356
85371
  return lines.join(`
85357
85372
  `);
85358
85373
  }
85374
+ function synthesizeFinalCouncilAdvisory(projectSummary, verdicts, roundNumber, config3 = {}) {
85375
+ const cfg = { ...COUNCIL_DEFAULTS, ...config3 };
85376
+ const timestamp = new Date().toISOString();
85377
+ const quorumSize = new Set(verdicts.map((v) => v.agent)).size;
85378
+ const rejectingMembers = verdicts.filter((v) => v.verdict === "REJECT").map((v) => v.agent);
85379
+ let overallVerdict;
85380
+ if (cfg.vetoPriority && rejectingMembers.length > 0) {
85381
+ overallVerdict = "REJECT";
85382
+ } else if (verdicts.some((v) => v.verdict === "CONCERNS") || !cfg.vetoPriority && rejectingMembers.length > 0) {
85383
+ overallVerdict = "CONCERNS";
85384
+ } else {
85385
+ overallVerdict = "APPROVE";
85386
+ }
85387
+ const unresolvedConflicts = detectConflicts(verdicts);
85388
+ const rejectingSet = new Set(rejectingMembers);
85389
+ const vetoFindings = verdicts.filter((v) => rejectingSet.has(v.agent)).flatMap((v) => v.findings);
85390
+ const requiredFixes = vetoFindings.filter((f) => f.severity === "HIGH" || f.severity === "MEDIUM");
85391
+ const advisoryFindings = [
85392
+ ...vetoFindings.filter((f) => f.severity === "LOW"),
85393
+ ...verdicts.filter((v) => !rejectingSet.has(v.agent)).flatMap((v) => v.findings)
85394
+ ];
85395
+ const advisoryNotes = [];
85396
+ if (advisoryFindings.length > 0) {
85397
+ advisoryNotes.push(`Final council found ${advisoryFindings.length} advisory finding(s). Review before project close.`);
85398
+ }
85399
+ if (quorumSize < 3) {
85400
+ advisoryNotes.push(`Final council quorum is ${quorumSize} members - dispatch additional project-scoped council members before closing the project.`);
85401
+ }
85402
+ const allUnmetIds = new Set(verdicts.flatMap((v) => v.criteriaUnmet));
85403
+ const allCriteriaMet = allUnmetIds.size === 0 && verdicts.length > 0;
85404
+ const unifiedFeedbackMd = buildFinalCouncilFeedback(projectSummary, overallVerdict, rejectingMembers, requiredFixes, advisoryFindings, unresolvedConflicts, roundNumber, cfg.maxRounds);
85405
+ return {
85406
+ scope: "project",
85407
+ timestamp,
85408
+ overallVerdict,
85409
+ vetoedBy: rejectingMembers.length > 0 ? rejectingMembers : null,
85410
+ memberVerdicts: verdicts,
85411
+ unresolvedConflicts,
85412
+ requiredFixes,
85413
+ advisoryFindings,
85414
+ advisoryNotes,
85415
+ unifiedFeedbackMd,
85416
+ roundNumber,
85417
+ allCriteriaMet,
85418
+ quorumSize,
85419
+ evidencePath: ".swarm/evidence/final-council.json",
85420
+ projectSummary
85421
+ };
85422
+ }
85423
+ function buildFinalCouncilFeedback(projectSummary, verdict, vetoedBy, requiredFixes, advisoryFindings, conflicts, roundNumber, maxRounds) {
85424
+ const lines = [
85425
+ `## Final Council Review - Round ${roundNumber}/${maxRounds}`,
85426
+ `**Scope:** completed project **Overall verdict:** ${verdict}`,
85427
+ ""
85428
+ ];
85429
+ if (projectSummary) {
85430
+ lines.push(`**Project Summary:** ${projectSummary}`);
85431
+ lines.push("");
85432
+ }
85433
+ if (vetoedBy.length > 0) {
85434
+ lines.push(`> BLOCKED: project close is blocked by ${vetoedBy.join(", ")}`);
85435
+ lines.push("");
85436
+ }
85437
+ if (requiredFixes.length > 0) {
85438
+ lines.push("### Required Fixes (must resolve before project close)");
85439
+ for (const f of requiredFixes) {
85440
+ lines.push(`- **[${f.severity}]** \`${f.location}\` - ${f.detail}`, ` _Evidence:_ ${f.evidence}`);
85441
+ }
85442
+ lines.push("");
85443
+ }
85444
+ if (conflicts.length > 0) {
85445
+ lines.push("### Conflicts to Resolve");
85446
+ lines.push("_The following council members gave contradictory project-close instructions. Architect must resolve before closing the project._");
85447
+ for (const c of conflicts) {
85448
+ lines.push(`- ${c}`);
85449
+ }
85450
+ lines.push("");
85451
+ }
85452
+ if (advisoryFindings.length > 0) {
85453
+ lines.push("### Advisory Findings (non-blocking)");
85454
+ for (const f of advisoryFindings) {
85455
+ lines.push(`- **[${f.severity}]** \`${f.location}\` - ${f.detail}`);
85456
+ }
85457
+ lines.push("");
85458
+ }
85459
+ if (verdict === "APPROVE") {
85460
+ lines.push("> Final council approved. Project may proceed to close.");
85461
+ } else if (roundNumber >= maxRounds) {
85462
+ lines.push(`> Max rounds (${maxRounds}) reached. Escalate to user - do not close the project automatically.`);
85463
+ }
85464
+ return lines.join(`
85465
+ `);
85466
+ }
85359
85467
 
85360
85468
  // src/council/criteria-store.ts
85361
85469
  import { existsSync as existsSync50, mkdirSync as mkdirSync23, readFileSync as readFileSync42, writeFileSync as writeFileSync16 } from "node:fs";
@@ -88777,7 +88885,7 @@ function listLaneEvidenceSync(directory, phase) {
88777
88885
  }
88778
88886
  return laneIds;
88779
88887
  }
88780
- var _internals36 = {
88888
+ var _internals37 = {
88781
88889
  listActiveLocks,
88782
88890
  readPersisted: readPersisted2,
88783
88891
  readPlanJson: defaultReadPlanJson,
@@ -88838,7 +88946,7 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
88838
88946
  reason: "Lean Turbo state unreadable or missing"
88839
88947
  };
88840
88948
  }
88841
- const persisted = _internals36.readPersisted(directory);
88949
+ const persisted = _internals37.readPersisted(directory);
88842
88950
  if (!persisted) {
88843
88951
  return {
88844
88952
  ok: false,
@@ -88902,7 +89010,7 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
88902
89010
  }
88903
89011
  }
88904
89012
  if (runState.lanes.length > 0) {
88905
- const evidenceLaneIds = new Set(_internals36.listLaneEvidenceSync(directory, phase));
89013
+ const evidenceLaneIds = new Set(_internals37.listLaneEvidenceSync(directory, phase));
88906
89014
  for (const lane of runState.lanes) {
88907
89015
  if ((lane.status === "completed" || lane.status === "failed") && !evidenceLaneIds.has(lane.laneId)) {
88908
89016
  return {
@@ -88912,7 +89020,7 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
88912
89020
  }
88913
89021
  }
88914
89022
  }
88915
- const activeLocks = _internals36.listActiveLocks(directory);
89023
+ const activeLocks = _internals37.listActiveLocks(directory);
88916
89024
  const phaseLaneIds = new Set(laneIds);
88917
89025
  for (const lock of activeLocks) {
88918
89026
  if (lock.laneId && phaseLaneIds.has(lock.laneId)) {
@@ -88932,7 +89040,7 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
88932
89040
  }
88933
89041
  const serialDegradedTasks = runState.degradedTasks.filter((dt) => !laneTaskIds.has(dt.taskId));
88934
89042
  if (serialDegradedTasks.length > 0) {
88935
- const plan = _internals36.readPlanJson(directory);
89043
+ const plan = _internals37.readPlanJson(directory);
88936
89044
  if (!plan) {
88937
89045
  return {
88938
89046
  ok: false,
@@ -88976,7 +89084,7 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
88976
89084
  }
88977
89085
  const serializedTasks = runState.serializedTasks;
88978
89086
  if (Array.isArray(serializedTasks) && serializedTasks.length > 0) {
88979
- const plan = _internals36.readPlanJson(directory);
89087
+ const plan = _internals37.readPlanJson(directory);
88980
89088
  if (!plan) {
88981
89089
  return {
88982
89090
  ok: false,
@@ -89035,7 +89143,7 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
89035
89143
  }
89036
89144
  let reviewerVerdict = runState.lastReviewerVerdict;
89037
89145
  if (!reviewerVerdict) {
89038
- const evidence = _internals36.readReviewerEvidence(directory, phase);
89146
+ const evidence = _internals37.readReviewerEvidence(directory, phase);
89039
89147
  reviewerVerdict = evidence?.verdict ?? undefined;
89040
89148
  }
89041
89149
  if (mergedConfig.phase_reviewer) {
@@ -89048,7 +89156,7 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
89048
89156
  }
89049
89157
  let criticVerdict = runState.lastCriticVerdict;
89050
89158
  if (!criticVerdict) {
89051
- const evidence = _internals36.readCriticEvidence(directory, phase);
89159
+ const evidence = _internals37.readCriticEvidence(directory, phase);
89052
89160
  criticVerdict = evidence?.verdict ?? undefined;
89053
89161
  }
89054
89162
  if (mergedConfig.phase_critic) {
@@ -89823,6 +89931,41 @@ Advisory notes: ${advisoryNotes.join("; ")}` : "";
89823
89931
  }, null, 2);
89824
89932
  }
89825
89933
  }
89934
+ if (typeof entry.quorumSize !== "number" || !Number.isFinite(entry.quorumSize) || entry.quorumSize < 5) {
89935
+ return JSON.stringify({
89936
+ success: false,
89937
+ phase,
89938
+ status: "blocked",
89939
+ reason: "FINAL_COUNCIL_MISSING_QUORUM",
89940
+ message: `Phase ${phase} (last phase) cannot be completed: final council evidence is missing valid quorum metadata. Re-run the project-scoped five-member final council and call write_final_council_evidence to generate quorumed evidence.`,
89941
+ agentsDispatched,
89942
+ agentsMissing: [],
89943
+ warnings: []
89944
+ }, null, 2);
89945
+ }
89946
+ const requiredFinalCouncilMembers = [
89947
+ "critic",
89948
+ "reviewer",
89949
+ "sme",
89950
+ "test_engineer",
89951
+ "explorer"
89952
+ ];
89953
+ const membersVoted = Array.isArray(entry.membersVoted) ? entry.membersVoted.filter((member) => typeof member === "string") : [];
89954
+ const membersAbsent = Array.isArray(entry.membersAbsent) ? entry.membersAbsent.filter((member) => typeof member === "string") : [];
89955
+ const distinctMembersVoted = new Set(membersVoted);
89956
+ const hasAllRequiredMembers = requiredFinalCouncilMembers.every((member) => distinctMembersVoted.has(member)) && distinctMembersVoted.size === requiredFinalCouncilMembers.length && membersAbsent.length === 0;
89957
+ if (!hasAllRequiredMembers) {
89958
+ return JSON.stringify({
89959
+ success: false,
89960
+ phase,
89961
+ status: "blocked",
89962
+ reason: "FINAL_COUNCIL_MISSING_QUORUM",
89963
+ message: `Phase ${phase} (last phase) cannot be completed: final council evidence does not prove all five required members voted. Re-run the project-scoped five-member final council and call write_final_council_evidence to generate complete evidence.`,
89964
+ agentsDispatched,
89965
+ agentsMissing: [],
89966
+ warnings: []
89967
+ }, null, 2);
89968
+ }
89826
89969
  if (entry.verdict === "rejected" || entry.verdict === "REJECTED") {
89827
89970
  return JSON.stringify({
89828
89971
  success: false,
@@ -89862,11 +90005,11 @@ Advisory notes: ${advisoryNotes.join("; ")}` : "";
89862
90005
  status: "blocked",
89863
90006
  reason: "FINAL_COUNCIL_REQUIRED",
89864
90007
  final_council_required: true,
89865
- message: `Phase ${phase} (last phase) cannot be completed: final_council is enabled and final council evidence not found at .swarm/evidence/final-council.json. Convene a final holistic council (use convene_general_council with mode 'general') and call write_final_council_evidence to persist the verdict before completing the project.`,
90008
+ message: `Phase ${phase} (last phase) cannot be completed: final_council is enabled and final council evidence not found at .swarm/evidence/final-council.json. Dispatch critic, reviewer, sme, test_engineer, and explorer with project-scoped context, collect their CouncilMemberVerdict JSON, and call write_final_council_evidence before completing the project. Do not use convene_general_council for this gate.`,
89866
90009
  agentsDispatched,
89867
90010
  agentsMissing: [],
89868
90011
  warnings: [
89869
- `Final council required convene a holistic project review using convene_general_council, then call write_final_council_evidence to persist the verdict.`
90012
+ `Final council required - dispatch the five project-scoped council members, then call write_final_council_evidence to persist quorumed evidence.`
89870
90013
  ]
89871
90014
  }, null, 2);
89872
90015
  }
@@ -89916,7 +90059,7 @@ Advisory notes: ${advisoryNotes.join("; ")}` : "";
89916
90059
  phase_critic: leanConfig.phase_critic,
89917
90060
  integrated_diff_required: leanConfig.integrated_diff_required
89918
90061
  } : undefined;
89919
- const leanCheck = _internals36.verifyLeanTurboPhaseReady(dir, phase, sessionID, leanPhaseReadyConfig);
90062
+ const leanCheck = _internals37.verifyLeanTurboPhaseReady(dir, phase, sessionID, leanPhaseReadyConfig);
89920
90063
  if (!leanCheck.ok) {
89921
90064
  return JSON.stringify({
89922
90065
  success: false,
@@ -92104,11 +92247,11 @@ var quality_budget = createSwarmTool({
92104
92247
  }).optional().describe("Quality budget thresholds")
92105
92248
  },
92106
92249
  async execute(args2, directory) {
92107
- const result = await _internals37.qualityBudget(args2, directory);
92250
+ const result = await _internals38.qualityBudget(args2, directory);
92108
92251
  return JSON.stringify(result);
92109
92252
  }
92110
92253
  });
92111
- var _internals37 = {
92254
+ var _internals38 = {
92112
92255
  qualityBudget
92113
92256
  };
92114
92257
 
@@ -92837,7 +92980,7 @@ import * as path109 from "node:path";
92837
92980
  var semgrepAvailableCache = null;
92838
92981
  var DEFAULT_RULES_DIR = ".swarm/semgrep-rules";
92839
92982
  var DEFAULT_TIMEOUT_MS3 = 30000;
92840
- var _internals38 = {
92983
+ var _internals39 = {
92841
92984
  isSemgrepAvailable,
92842
92985
  checkSemgrepAvailable,
92843
92986
  resetSemgrepCache,
@@ -92862,7 +93005,7 @@ function isSemgrepAvailable() {
92862
93005
  }
92863
93006
  }
92864
93007
  async function checkSemgrepAvailable() {
92865
- return _internals38.isSemgrepAvailable();
93008
+ return _internals39.isSemgrepAvailable();
92866
93009
  }
92867
93010
  function resetSemgrepCache() {
92868
93011
  semgrepAvailableCache = null;
@@ -92959,12 +93102,12 @@ async function runSemgrep(options) {
92959
93102
  const timeoutMs = options.timeoutMs || DEFAULT_TIMEOUT_MS3;
92960
93103
  if (files.length === 0) {
92961
93104
  return {
92962
- available: _internals38.isSemgrepAvailable(),
93105
+ available: _internals39.isSemgrepAvailable(),
92963
93106
  findings: [],
92964
93107
  engine: "tier_a"
92965
93108
  };
92966
93109
  }
92967
- if (!_internals38.isSemgrepAvailable()) {
93110
+ if (!_internals39.isSemgrepAvailable()) {
92968
93111
  return {
92969
93112
  available: false,
92970
93113
  findings: [],
@@ -93123,7 +93266,7 @@ function assignOccurrenceIndices(findings, directory) {
93123
93266
  }
93124
93267
  const occIdx = countMap.get(baseKey) ?? 0;
93125
93268
  countMap.set(baseKey, occIdx + 1);
93126
- const fp = _internals39.fingerprintFinding(finding, directory, occIdx);
93269
+ const fp = _internals40.fingerprintFinding(finding, directory, occIdx);
93127
93270
  return {
93128
93271
  finding,
93129
93272
  index: occIdx,
@@ -93192,7 +93335,7 @@ async function captureOrMergeBaseline(directory, phase, findings, engine, scanne
93192
93335
  }
93193
93336
  } catch {}
93194
93337
  const scannedRelFiles = new Set(scannedFiles.map((f) => normalizeFindingPath(directory, f)));
93195
- const indexed = _internals39.assignOccurrenceIndices(findings, directory);
93338
+ const indexed = _internals40.assignOccurrenceIndices(findings, directory);
93196
93339
  if (existing && !opts?.force) {
93197
93340
  const prunedFingerprints = existing.fingerprints.filter((fp) => {
93198
93341
  const relFile = fp.slice(0, fp.indexOf("|"));
@@ -93332,7 +93475,7 @@ function loadBaseline(directory, phase) {
93332
93475
  };
93333
93476
  }
93334
93477
  }
93335
- var _internals39 = {
93478
+ var _internals40 = {
93336
93479
  fingerprintFinding,
93337
93480
  assignOccurrenceIndices,
93338
93481
  captureOrMergeBaseline,
@@ -93742,11 +93885,11 @@ var sast_scan = createSwarmTool({
93742
93885
  capture_baseline: safeArgs.capture_baseline,
93743
93886
  phase: safeArgs.phase
93744
93887
  };
93745
- const result = await _internals40.sastScan(input, directory);
93888
+ const result = await _internals41.sastScan(input, directory);
93746
93889
  return JSON.stringify(result, null, 2);
93747
93890
  }
93748
93891
  });
93749
- var _internals40 = {
93892
+ var _internals41 = {
93750
93893
  sastScan,
93751
93894
  sast_scan
93752
93895
  };
@@ -97343,7 +97486,7 @@ var set_qa_gates = createSwarmTool({
97343
97486
  mutation_test: exports_external.boolean().optional().describe("Enable the mutation-testing gate (default: off). Requires mutation " + "tests to achieve a passing kill rate before phase completion; " + "WARN verdict allows advancement, FAIL blocks."),
97344
97487
  council_general_review: exports_external.boolean().optional().describe("Enable the council_general_review gate (default: off). When on, " + "MODE: SPECIFY runs convene_general_council on the draft spec " + "before the critic-gate, folding multi-model deliberation into " + "the spec. Requires council.general.enabled and a search API key."),
97345
97488
  drift_check: exports_external.boolean().optional().describe("Enable drift verification gate (default: on). Blocks phase_complete " + "until drift-verifier.json has an approved verdict. When disabled, " + "drift verification is skipped entirely."),
97346
- final_council: exports_external.boolean().optional().describe("Enable the final_council gate (default: off). When on, " + "after all phases complete the architect runs a holistic " + "general council review against the entire body of work. " + "Requires council.general.enabled: true in plugin config."),
97489
+ final_council: exports_external.boolean().optional().describe("Enable the final_council gate (default: off). When on, " + "after all phases complete the architect dispatches critic, reviewer, " + "sme, test_engineer, and explorer with project-scoped context, " + "collects their CouncilMemberVerdict objects, and calls " + "write_final_council_evidence. This is not General Council mode " + "and does not require council.general.enabled."),
97347
97490
  project_type: exports_external.string().optional().describe('Project type label (e.g. "ts", "python"). Only applied when the profile is being created for the first time.')
97348
97491
  },
97349
97492
  execute: async (args2, directory) => {
@@ -98929,7 +99072,7 @@ function resolveDefaultReviewerAgent(generatedAgentNames) {
98929
99072
  }
98930
99073
  async function compileReviewPackage(directory, phase, sessionID, requireDiffSummary) {
98931
99074
  const lanes = await listLaneEvidence(directory, phase);
98932
- const persisted = _internals41.readPersisted?.(directory) ?? null;
99075
+ const persisted = _internals42.readPersisted?.(directory) ?? null;
98933
99076
  if (persisted) {
98934
99077
  let matchingRunState = null;
98935
99078
  for (const sessionState of Object.values(persisted.sessions)) {
@@ -99121,7 +99264,7 @@ Be specific and evidence-based. Do not approve a phase with unresolved degraded
99121
99264
  client.session.delete({ path: { id: sessionId } }).catch(() => {});
99122
99265
  }
99123
99266
  }
99124
- var _internals41 = {
99267
+ var _internals42 = {
99125
99268
  compileReviewPackage,
99126
99269
  parseReviewerVerdict,
99127
99270
  writeReviewerEvidence,
@@ -99138,28 +99281,28 @@ async function dispatchPhaseReviewer(directory, phase, sessionID, config3) {
99138
99281
  };
99139
99282
  const generatedAgentNames = swarmState.generatedAgentNames;
99140
99283
  const agentName = mergedConfig.reviewerAgent || resolveDefaultReviewerAgent(generatedAgentNames);
99141
- const pkg = await _internals41.compileReviewPackage(directory, phase, sessionID, mergedConfig.requireDiffSummary);
99284
+ const pkg = await _internals42.compileReviewPackage(directory, phase, sessionID, mergedConfig.requireDiffSummary);
99142
99285
  let responseText;
99143
99286
  try {
99144
- responseText = await _internals41.dispatchReviewerAgent(directory, pkg, agentName, mergedConfig.timeoutMs);
99287
+ responseText = await _internals42.dispatchReviewerAgent(directory, pkg, agentName, mergedConfig.timeoutMs);
99145
99288
  } catch (error93) {
99146
- const evidencePath2 = await _internals41.writeReviewerEvidence(directory, phase, "REJECTED", error93 instanceof Error ? error93.message : String(error93));
99289
+ const evidencePath2 = await _internals42.writeReviewerEvidence(directory, phase, "REJECTED", error93 instanceof Error ? error93.message : String(error93));
99147
99290
  return {
99148
99291
  verdict: "REJECTED",
99149
99292
  reason: `Reviewer dispatch failed: ${error93 instanceof Error ? error93.message : String(error93)}`,
99150
99293
  evidencePath: evidencePath2
99151
99294
  };
99152
99295
  }
99153
- const parsed = _internals41.parseReviewerVerdict(responseText);
99296
+ const parsed = _internals42.parseReviewerVerdict(responseText);
99154
99297
  if (!parsed) {
99155
- const evidencePath2 = await _internals41.writeReviewerEvidence(directory, phase, "REJECTED", "Reviewer response could not be parsed");
99298
+ const evidencePath2 = await _internals42.writeReviewerEvidence(directory, phase, "REJECTED", "Reviewer response could not be parsed");
99156
99299
  return {
99157
99300
  verdict: "REJECTED",
99158
99301
  reason: "Reviewer response could not be parsed",
99159
99302
  evidencePath: evidencePath2
99160
99303
  };
99161
99304
  }
99162
- const evidencePath = await _internals41.writeReviewerEvidence(directory, phase, parsed.verdict, parsed.reason);
99305
+ const evidencePath = await _internals42.writeReviewerEvidence(directory, phase, parsed.verdict, parsed.reason);
99163
99306
  return {
99164
99307
  verdict: parsed.verdict,
99165
99308
  reason: parsed.reason,
@@ -99665,7 +99808,7 @@ ${fileList}
99665
99808
 
99666
99809
  // src/tools/lean-turbo-run-phase.ts
99667
99810
  init_create_tool();
99668
- var _internals42 = {
99811
+ var _internals43 = {
99669
99812
  LeanTurboRunner,
99670
99813
  loadPluginConfigWithMeta
99671
99814
  };
@@ -99675,9 +99818,9 @@ async function executeLeanTurboRunPhase(args2) {
99675
99818
  let runError = null;
99676
99819
  let runner = null;
99677
99820
  try {
99678
- const { config: config3 } = _internals42.loadPluginConfigWithMeta(directory);
99821
+ const { config: config3 } = _internals43.loadPluginConfigWithMeta(directory);
99679
99822
  const leanConfig = config3.turbo?.strategy === "lean" ? config3.turbo.lean : undefined;
99680
- runner = new _internals42.LeanTurboRunner({
99823
+ runner = new _internals43.LeanTurboRunner({
99681
99824
  directory,
99682
99825
  sessionID,
99683
99826
  opencodeClient: swarmState.opencodeClient ?? null,
@@ -100031,7 +100174,7 @@ function isStaticallyEquivalent(originalCode, mutatedCode) {
100031
100174
  const strippedMutated = stripCode(mutatedCode);
100032
100175
  return strippedOriginal === strippedMutated;
100033
100176
  }
100034
- var _internals43 = {
100177
+ var _internals44 = {
100035
100178
  isStaticallyEquivalent,
100036
100179
  checkEquivalence,
100037
100180
  batchCheckEquivalence
@@ -100071,7 +100214,7 @@ async function batchCheckEquivalence(patches, llmJudge) {
100071
100214
  const results = [];
100072
100215
  for (const { patch, originalCode, mutatedCode } of patches) {
100073
100216
  try {
100074
- const result = await _internals43.checkEquivalence(patch, originalCode, mutatedCode, llmJudge);
100217
+ const result = await _internals44.checkEquivalence(patch, originalCode, mutatedCode, llmJudge);
100075
100218
  results.push(result);
100076
100219
  } catch (err3) {
100077
100220
  results.push({
@@ -100371,7 +100514,7 @@ async function executeMutationSuite(patches, testCommand, testFiles, workingDir,
100371
100514
  }
100372
100515
 
100373
100516
  // src/mutation/gate.ts
100374
- var _internals44 = {
100517
+ var _internals45 = {
100375
100518
  evaluateMutationGate,
100376
100519
  buildTestImprovementPrompt,
100377
100520
  buildMessage
@@ -100392,8 +100535,8 @@ function evaluateMutationGate(report, passThreshold = PASS_THRESHOLD, warnThresh
100392
100535
  } else {
100393
100536
  verdict = "fail";
100394
100537
  }
100395
- const testImprovementPrompt = _internals44.buildTestImprovementPrompt(report, passThreshold, verdict);
100396
- const message = _internals44.buildMessage(verdict, adjustedKillRate, report.killed, report.totalMutants, report.equivalent, warnThreshold);
100538
+ const testImprovementPrompt = _internals45.buildTestImprovementPrompt(report, passThreshold, verdict);
100539
+ const message = _internals45.buildMessage(verdict, adjustedKillRate, report.killed, report.totalMutants, report.equivalent, warnThreshold);
100397
100540
  return {
100398
100541
  verdict,
100399
100542
  killRate: report.killRate,
@@ -101010,7 +101153,7 @@ import * as path131 from "node:path";
101010
101153
  init_bun_compat();
101011
101154
  import * as fs102 from "node:fs";
101012
101155
  import * as path130 from "node:path";
101013
- var _internals45 = { bunSpawn };
101156
+ var _internals46 = { bunSpawn };
101014
101157
  var _swarmGitExcludedChecked = false;
101015
101158
  function fileCoversSwarm(content) {
101016
101159
  for (const rawLine of content.split(`
@@ -101043,7 +101186,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
101043
101186
  checkIgnoreExitCode
101044
101187
  ] = await Promise.all([
101045
101188
  (async () => {
101046
- const proc = _internals45.bunSpawn(["git", "-C", directory, "rev-parse", "--show-toplevel"], GIT_SPAWN_OPTIONS);
101189
+ const proc = _internals46.bunSpawn(["git", "-C", directory, "rev-parse", "--show-toplevel"], GIT_SPAWN_OPTIONS);
101047
101190
  try {
101048
101191
  return await Promise.all([proc.exited, proc.stdout.text()]);
101049
101192
  } finally {
@@ -101053,7 +101196,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
101053
101196
  }
101054
101197
  })(),
101055
101198
  (async () => {
101056
- const proc = _internals45.bunSpawn(["git", "-C", directory, "rev-parse", "--git-path", "info/exclude"], GIT_SPAWN_OPTIONS);
101199
+ const proc = _internals46.bunSpawn(["git", "-C", directory, "rev-parse", "--git-path", "info/exclude"], GIT_SPAWN_OPTIONS);
101057
101200
  try {
101058
101201
  return await Promise.all([proc.exited, proc.stdout.text()]);
101059
101202
  } finally {
@@ -101063,7 +101206,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
101063
101206
  }
101064
101207
  })(),
101065
101208
  (async () => {
101066
- const proc = _internals45.bunSpawn(["git", "-C", directory, "check-ignore", "-q", ".swarm/.gitkeep"], GIT_SPAWN_OPTIONS);
101209
+ const proc = _internals46.bunSpawn(["git", "-C", directory, "check-ignore", "-q", ".swarm/.gitkeep"], GIT_SPAWN_OPTIONS);
101067
101210
  try {
101068
101211
  return await proc.exited;
101069
101212
  } finally {
@@ -101102,7 +101245,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
101102
101245
  }
101103
101246
  } catch {}
101104
101247
  }
101105
- const trackedProc = _internals45.bunSpawn(["git", "-C", directory, "ls-files", "--", ".swarm"], GIT_SPAWN_OPTIONS);
101248
+ const trackedProc = _internals46.bunSpawn(["git", "-C", directory, "ls-files", "--", ".swarm"], GIT_SPAWN_OPTIONS);
101106
101249
  let trackedExitCode;
101107
101250
  let trackedOutput;
101108
101251
  try {
@@ -101127,7 +101270,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
101127
101270
  }
101128
101271
 
101129
101272
  // src/hooks/diff-scope.ts
101130
- var _internals46 = { bunSpawn };
101273
+ var _internals47 = { bunSpawn };
101131
101274
  function getDeclaredScope(taskId, directory) {
101132
101275
  try {
101133
101276
  const planPath = path131.join(directory, ".swarm", "plan.json");
@@ -101162,7 +101305,7 @@ var GIT_DIFF_SPAWN_OPTIONS = {
101162
101305
  };
101163
101306
  async function getChangedFiles(directory) {
101164
101307
  try {
101165
- const proc = _internals46.bunSpawn(["git", "diff", "--name-only", "HEAD~1"], {
101308
+ const proc = _internals47.bunSpawn(["git", "diff", "--name-only", "HEAD~1"], {
101166
101309
  cwd: directory,
101167
101310
  ...GIT_DIFF_SPAWN_OPTIONS
101168
101311
  });
@@ -101179,7 +101322,7 @@ async function getChangedFiles(directory) {
101179
101322
  return stdout.trim().split(`
101180
101323
  `).map((f) => f.trim()).filter((f) => f.length > 0);
101181
101324
  }
101182
- const proc2 = _internals46.bunSpawn(["git", "diff", "--name-only", "HEAD"], {
101325
+ const proc2 = _internals47.bunSpawn(["git", "diff", "--name-only", "HEAD"], {
101183
101326
  cwd: directory,
101184
101327
  ...GIT_DIFF_SPAWN_OPTIONS
101185
101328
  });
@@ -101237,7 +101380,7 @@ init_telemetry();
101237
101380
  init_file_locks();
101238
101381
  import * as fs104 from "node:fs";
101239
101382
  import * as path132 from "node:path";
101240
- var _internals47 = {
101383
+ var _internals48 = {
101241
101384
  listActiveLocks,
101242
101385
  verifyLeanTurboTaskCompletion
101243
101386
  };
@@ -101379,7 +101522,7 @@ function verifyLeanTurboTaskCompletion(directory, taskId, sessionID) {
101379
101522
  }
101380
101523
  };
101381
101524
  }
101382
- const activeLocks = _internals47.listActiveLocks(directory);
101525
+ const activeLocks = _internals48.listActiveLocks(directory);
101383
101526
  const laneLocks = activeLocks.filter((lock) => lock.laneId === lane.laneId);
101384
101527
  if (laneLocks.length > 0) {
101385
101528
  return {
@@ -102301,59 +102444,94 @@ var write_drift_evidence = createSwarmTool({
102301
102444
  });
102302
102445
  // src/tools/write-final-council-evidence.ts
102303
102446
  init_zod();
102447
+ init_loader();
102448
+ import fs107 from "node:fs";
102449
+ import path135 from "node:path";
102304
102450
  init_utils2();
102305
102451
  init_manager();
102306
102452
  init_create_tool();
102307
- import fs107 from "node:fs";
102308
- import path135 from "node:path";
102309
- function normalizeVerdict2(verdict) {
102310
- switch (verdict) {
102311
- case "APPROVED":
102312
- return "approved";
102313
- case "NEEDS_REVISION":
102314
- return "rejected";
102315
- default:
102316
- throw new Error(`Invalid verdict: must be 'APPROVED' or 'NEEDS_REVISION', got '${verdict}'`);
102317
- }
102453
+ var FINAL_COUNCIL_MEMBERS = [
102454
+ "critic",
102455
+ "reviewer",
102456
+ "sme",
102457
+ "test_engineer",
102458
+ "explorer"
102459
+ ];
102460
+ var VerdictSchema3 = exports_external.object({
102461
+ agent: exports_external.enum(FINAL_COUNCIL_MEMBERS),
102462
+ verdict: exports_external.enum(["APPROVE", "CONCERNS", "REJECT"]),
102463
+ confidence: exports_external.number().min(0).max(1),
102464
+ findings: exports_external.array(exports_external.object({
102465
+ severity: exports_external.enum(["HIGH", "MEDIUM", "LOW"]),
102466
+ category: exports_external.string().min(1),
102467
+ location: exports_external.string(),
102468
+ detail: exports_external.string(),
102469
+ evidence: exports_external.string()
102470
+ })),
102471
+ criteriaAssessed: exports_external.array(exports_external.string()),
102472
+ criteriaUnmet: exports_external.array(exports_external.string()),
102473
+ durationMs: exports_external.number().nonnegative()
102474
+ });
102475
+ var ArgsSchema6 = exports_external.object({
102476
+ phase: exports_external.number().int().min(1),
102477
+ projectSummary: exports_external.string().min(1),
102478
+ roundNumber: exports_external.number().int().min(1).max(10).optional(),
102479
+ verdicts: exports_external.array(VerdictSchema3).min(1).max(5)
102480
+ });
102481
+ function normalizeFinalVerdict(verdict) {
102482
+ return verdict === "APPROVE" ? "approved" : "rejected";
102318
102483
  }
102319
102484
  async function executeWriteFinalCouncilEvidence(args2, directory) {
102320
- const phase = args2.phase;
102321
- if (!Number.isInteger(phase) || phase < 1) {
102322
- return JSON.stringify({
102323
- success: false,
102324
- phase,
102325
- message: "Invalid phase: must be a positive integer"
102326
- }, null, 2);
102327
- }
102328
- const validVerdicts = ["APPROVED", "NEEDS_REVISION"];
102329
- if (!validVerdicts.includes(args2.verdict)) {
102485
+ const parsed = ArgsSchema6.safeParse(args2);
102486
+ if (!parsed.success) {
102330
102487
  return JSON.stringify({
102331
102488
  success: false,
102332
- phase,
102333
- message: "Invalid verdict: must be 'APPROVED' or 'NEEDS_REVISION'"
102489
+ reason: "invalid arguments",
102490
+ errors: parsed.error.issues.map((i2) => ({
102491
+ path: i2.path.join("."),
102492
+ message: i2.message
102493
+ }))
102334
102494
  }, null, 2);
102335
102495
  }
102336
- const summary = args2.summary;
102337
- if (typeof summary !== "string" || summary.trim().length === 0) {
102496
+ const input = parsed.data;
102497
+ const config3 = loadPluginConfig(directory);
102498
+ const requiredMembers = FINAL_COUNCIL_MEMBERS.length;
102499
+ const distinctMembers = new Set(input.verdicts.map((v) => v.agent));
102500
+ const membersVoted = [...distinctMembers];
102501
+ const membersAbsent = FINAL_COUNCIL_MEMBERS.filter((m) => !distinctMembers.has(m));
102502
+ if (membersVoted.length < requiredMembers) {
102338
102503
  return JSON.stringify({
102339
102504
  success: false,
102340
- phase,
102341
- message: "Invalid summary: must be a non-empty string"
102505
+ reason: "insufficient_quorum",
102506
+ message: `Final council quorum not met: ${membersVoted.length} of ${requiredMembers} required members provided verdicts. ` + `Members voted: [${membersVoted.join(", ")}]. ` + `Members absent: [${membersAbsent.join(", ")}]. ` + `Dispatch the absent council members with project-scoped context and collect their verdicts before calling write_final_council_evidence.`,
102507
+ membersVoted,
102508
+ membersAbsent,
102509
+ quorumRequired: requiredMembers
102342
102510
  }, null, 2);
102343
102511
  }
102344
- const normalizedVerdict = normalizeVerdict2(args2.verdict);
102512
+ const synthesis = synthesizeFinalCouncilAdvisory(input.projectSummary.trim(), input.verdicts, input.roundNumber ?? 1, config3.council);
102345
102513
  const plan = await loadPlan(directory);
102346
102514
  const planId = plan ? derivePlanId(plan) : "unknown";
102515
+ const normalizedVerdict = normalizeFinalVerdict(synthesis.overallVerdict);
102347
102516
  const evidenceEntry = {
102348
102517
  type: "final-council",
102349
- phase,
102518
+ phase: input.phase,
102350
102519
  plan_id: planId,
102351
102520
  verdict: normalizedVerdict,
102352
- summary: summary.trim(),
102353
- timestamp: new Date().toISOString()
102354
- };
102355
- const evidenceContent = {
102356
- entries: [evidenceEntry]
102521
+ rawCouncilVerdict: synthesis.overallVerdict,
102522
+ quorumSize: synthesis.quorumSize,
102523
+ membersVoted,
102524
+ membersAbsent,
102525
+ requiredFixes: synthesis.requiredFixes,
102526
+ advisoryFindings: synthesis.advisoryFindings,
102527
+ advisoryNotes: synthesis.advisoryNotes,
102528
+ unresolvedConflicts: synthesis.unresolvedConflicts,
102529
+ roundNumber: synthesis.roundNumber,
102530
+ allCriteriaMet: synthesis.allCriteriaMet,
102531
+ memberVerdicts: synthesis.memberVerdicts,
102532
+ unifiedFeedbackMd: synthesis.unifiedFeedbackMd,
102533
+ projectSummary: synthesis.projectSummary,
102534
+ timestamp: synthesis.timestamp
102357
102535
  };
102358
102536
  const filename = "final-council.json";
102359
102537
  const relativePath = path135.join("evidence", filename);
@@ -102363,10 +102541,13 @@ async function executeWriteFinalCouncilEvidence(args2, directory) {
102363
102541
  } catch (error93) {
102364
102542
  return JSON.stringify({
102365
102543
  success: false,
102366
- phase,
102544
+ phase: input.phase,
102367
102545
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
102368
102546
  }, null, 2);
102369
102547
  }
102548
+ const evidenceContent = {
102549
+ entries: [evidenceEntry]
102550
+ };
102370
102551
  const evidenceDir = path135.dirname(validatedPath);
102371
102552
  try {
102372
102553
  await fs107.promises.mkdir(evidenceDir, { recursive: true });
@@ -102375,41 +102556,53 @@ async function executeWriteFinalCouncilEvidence(args2, directory) {
102375
102556
  await fs107.promises.rename(tempPath, validatedPath);
102376
102557
  return JSON.stringify({
102377
102558
  success: true,
102378
- phase,
102559
+ phase: input.phase,
102560
+ overallVerdict: synthesis.overallVerdict,
102379
102561
  verdict: normalizedVerdict,
102380
- message: `Final council evidence written to .swarm/evidence/final-council.json`
102562
+ vetoedBy: synthesis.vetoedBy,
102563
+ roundNumber: synthesis.roundNumber,
102564
+ allCriteriaMet: synthesis.allCriteriaMet,
102565
+ requiredFixesCount: synthesis.requiredFixes.length,
102566
+ advisoryFindingsCount: synthesis.advisoryFindings.length,
102567
+ unresolvedConflictsCount: synthesis.unresolvedConflicts.length,
102568
+ advisoryNotes: synthesis.advisoryNotes,
102569
+ membersVoted,
102570
+ membersAbsent,
102571
+ quorumSize: synthesis.quorumSize,
102572
+ quorumMet: true,
102573
+ evidencePath: synthesis.evidencePath,
102574
+ unifiedFeedbackMd: synthesis.unifiedFeedbackMd,
102575
+ message: "Final council evidence written to .swarm/evidence/final-council.json"
102381
102576
  }, null, 2);
102382
102577
  } catch (error93) {
102383
102578
  return JSON.stringify({
102384
102579
  success: false,
102385
- phase,
102580
+ phase: input.phase,
102386
102581
  message: error93 instanceof Error ? error93.message : String(error93)
102387
102582
  }, null, 2);
102388
102583
  }
102389
102584
  }
102390
102585
  var write_final_council_evidence = createSwarmTool({
102391
- description: "Write final council evidence for a completed project. Accepts phase, verdict (APPROVED/NEEDS_REVISION), summary, and writes structured evidence to .swarm/evidence/final-council.json. Normalizes verdict to lowercase. Use this after convening a final holistic council to persist the verdict.",
102586
+ description: "Write final council evidence for a completed project. This is not General Council mode and does not use convene_general_council. PREREQUISITE: dispatch critic, reviewer, sme, test_engineer, and explorer as project-scoped Agent tasks, collect their CouncilMemberVerdict JSON, then call this tool to synthesize and persist .swarm/evidence/final-council.json.",
102392
102587
  args: {
102393
- phase: exports_external.number().int().min(1).describe("The phase number for the final council verdict (e.g., 1, 2, 3)"),
102394
- verdict: exports_external.enum(["APPROVED", "NEEDS_REVISION"]).describe("Verdict of the final council: 'APPROVED' or 'NEEDS_REVISION'"),
102395
- summary: exports_external.string().describe("Human-readable summary of the final council verdict")
102588
+ phase: exports_external.number().int().min(1).describe("The final phase number for the project being reviewed"),
102589
+ projectSummary: exports_external.string().min(1).describe("Summary of the completed project and total work reviewed"),
102590
+ roundNumber: exports_external.number().int().min(1).max(10).optional().describe("1-indexed final council round number. Defaults to 1."),
102591
+ verdicts: exports_external.array(VerdictSchema3).min(1).max(5).describe("Collected CouncilMemberVerdict objects from critic, reviewer, sme, test_engineer, and explorer.")
102396
102592
  },
102397
102593
  execute: async (args2, directory) => {
102398
- const rawPhase = args2.phase !== undefined ? Number(args2.phase) : 0;
102399
- try {
102400
- const writeFinalCouncilEvidenceArgs = {
102401
- phase: Number(args2.phase),
102402
- verdict: String(args2.verdict),
102403
- summary: String(args2.summary ?? "")
102404
- };
102405
- return await executeWriteFinalCouncilEvidence(writeFinalCouncilEvidenceArgs, directory);
102406
- } catch (error93) {
102594
+ const parsed = ArgsSchema6.safeParse(args2);
102595
+ if (!parsed.success) {
102407
102596
  return JSON.stringify({
102408
102597
  success: false,
102409
- phase: rawPhase,
102410
- message: error93 instanceof Error ? error93.message : "Unknown error"
102598
+ reason: "invalid arguments",
102599
+ errors: parsed.error.issues.map((i2) => ({
102600
+ path: i2.path.join("."),
102601
+ message: i2.message
102602
+ }))
102411
102603
  }, null, 2);
102412
102604
  }
102605
+ return await executeWriteFinalCouncilEvidence(parsed.data, directory);
102413
102606
  }
102414
102607
  });
102415
102608
  // src/tools/write-hallucination-evidence.ts
@@ -102418,7 +102611,7 @@ init_utils2();
102418
102611
  init_create_tool();
102419
102612
  import fs108 from "node:fs";
102420
102613
  import path136 from "node:path";
102421
- function normalizeVerdict3(verdict) {
102614
+ function normalizeVerdict2(verdict) {
102422
102615
  switch (verdict) {
102423
102616
  case "APPROVED":
102424
102617
  return "approved";
@@ -102453,7 +102646,7 @@ async function executeWriteHallucinationEvidence(args2, directory) {
102453
102646
  message: "Invalid summary: must be a non-empty string"
102454
102647
  }, null, 2);
102455
102648
  }
102456
- const normalizedVerdict = normalizeVerdict3(args2.verdict);
102649
+ const normalizedVerdict = normalizeVerdict2(args2.verdict);
102457
102650
  const evidenceEntry = {
102458
102651
  type: "hallucination-verification",
102459
102652
  verdict: normalizedVerdict,
@@ -102529,7 +102722,7 @@ init_utils2();
102529
102722
  init_create_tool();
102530
102723
  import fs109 from "node:fs";
102531
102724
  import path137 from "node:path";
102532
- function normalizeVerdict4(verdict) {
102725
+ function normalizeVerdict3(verdict) {
102533
102726
  switch (verdict) {
102534
102727
  case "PASS":
102535
102728
  return "pass";
@@ -102586,7 +102779,7 @@ async function executeWriteMutationEvidence(args2, directory) {
102586
102779
  message: "Invalid summary: must be a non-empty string"
102587
102780
  }, null, 2);
102588
102781
  }
102589
- const normalizedVerdict = normalizeVerdict4(args2.verdict);
102782
+ const normalizedVerdict = normalizeVerdict3(args2.verdict);
102590
102783
  const evidenceEntry = {
102591
102784
  type: "mutation-gate",
102592
102785
  verdict: normalizedVerdict,