opencode-swarm 7.47.0 → 7.48.1

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
@@ -69,7 +69,7 @@ var package_default;
69
69
  var init_package = __esm(() => {
70
70
  package_default = {
71
71
  name: "opencode-swarm",
72
- version: "7.47.0",
72
+ version: "7.48.1",
73
73
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
74
74
  main: "dist/index.js",
75
75
  types: "dist/index.d.ts",
@@ -616,6 +616,7 @@ var init_constants = __esm(() => {
616
616
  "syntax_check",
617
617
  "search",
618
618
  "summarize_work",
619
+ "knowledge_recall",
619
620
  "swarm_command"
620
621
  ],
621
622
  sme: [
@@ -36016,12 +36017,13 @@ import { spawn, spawnSync } from "node:child_process";
36016
36017
  import * as fs8 from "node:fs";
36017
36018
  import * as os3 from "node:os";
36018
36019
  import * as path11 from "node:path";
36020
+ import { fileURLToPath } from "node:url";
36019
36021
  function findRunnerBinary() {
36020
36022
  const arch = process.arch === "x64" ? "x64" : "arm64";
36021
36023
  const platform = "win32";
36022
36024
  const packagePaths = [
36023
- path11.resolve(__dirname, "..", "..", "..", "binaries", `${platform}-${arch}`, "swarm-sandbox-runner.exe"),
36024
- path11.resolve(__dirname, "..", "..", "..", "..", "binaries", `${platform}-${arch}`, "swarm-sandbox-runner.exe")
36025
+ path11.resolve(_runtimeDir, "..", "..", "..", "binaries", `${platform}-${arch}`, "swarm-sandbox-runner.exe"),
36026
+ path11.resolve(_runtimeDir, "..", "..", "..", "..", "binaries", `${platform}-${arch}`, "swarm-sandbox-runner.exe")
36025
36027
  ];
36026
36028
  for (const p of packagePaths) {
36027
36029
  try {
@@ -36225,9 +36227,10 @@ function buildDefaultPolicy(workspaceRoot, runId) {
36225
36227
  deny_symlink_egress: true
36226
36228
  };
36227
36229
  }
36228
- var __dirname = "/home/runner/work/opencode-swarm/opencode-swarm/src/sandbox/win32", RUNNER_EXIT_CODES, _cachedProbe, _internals10;
36230
+ var _runtimeDir, RUNNER_EXIT_CODES, _cachedProbe, _internals10;
36229
36231
  var init_runner_client = __esm(() => {
36230
36232
  init_logger();
36233
+ _runtimeDir = fileURLToPath(new URL(".", import.meta.url));
36231
36234
  RUNNER_EXIT_CODES = {
36232
36235
  SUCCESS: 0,
36233
36236
  CHILD_NON_ZERO: 1,
@@ -62993,7 +62996,7 @@ var init_knowledge_diagnostics = __esm(() => {
62993
62996
  import * as child_process4 from "node:child_process";
62994
62997
  import { existsSync as existsSync21, readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync8 } from "node:fs";
62995
62998
  import path36 from "node:path";
62996
- import { fileURLToPath } from "node:url";
62999
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
62997
63000
  function validateTaskDag(plan) {
62998
63001
  const allTaskIds = new Set;
62999
63002
  for (const phase of plan.phases) {
@@ -63356,7 +63359,7 @@ async function checkGrammarWasmFiles() {
63356
63359
  "tree-sitter-ini.wasm",
63357
63360
  "tree-sitter-regex.wasm"
63358
63361
  ];
63359
- const thisDir = path36.dirname(fileURLToPath(import.meta.url));
63362
+ const thisDir = path36.dirname(fileURLToPath2(import.meta.url));
63360
63363
  const grammarDir = resolveGrammarDir(thisDir);
63361
63364
  const missing = [];
63362
63365
  if (!existsSync21(path36.join(grammarDir, "tree-sitter.wasm"))) {
@@ -72485,7 +72488,7 @@ var init_memory = __esm(() => {
72485
72488
  // src/commands/memory.ts
72486
72489
  import { existsSync as existsSync30 } from "node:fs";
72487
72490
  import * as path49 from "node:path";
72488
- import { fileURLToPath as fileURLToPath2 } from "node:url";
72491
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
72489
72492
  async function handleMemoryCommand(_directory, _args) {
72490
72493
  return [
72491
72494
  "## Swarm Memory",
@@ -72928,7 +72931,7 @@ var PACKAGE_ROOT;
72928
72931
  var init_memory2 = __esm(() => {
72929
72932
  init_loader();
72930
72933
  init_memory();
72931
- PACKAGE_ROOT = path49.resolve(resolvePackageRootFromModule(fileURLToPath2(import.meta.url)));
72934
+ PACKAGE_ROOT = path49.resolve(resolvePackageRootFromModule(fileURLToPath3(import.meta.url)));
72932
72935
  });
72933
72936
 
72934
72937
  // src/services/plan-service.ts
@@ -81429,17 +81432,13 @@ var init_tool_policy = __esm(() => {
81429
81432
  "agents",
81430
81433
  "config",
81431
81434
  "config doctor",
81432
- "config-doctor",
81433
- "doctor",
81434
81435
  "doctor tools",
81435
81436
  "status",
81436
81437
  "show-plan",
81437
- "plan",
81438
81438
  "help",
81439
81439
  "history",
81440
81440
  "evidence",
81441
81441
  "evidence summary",
81442
- "evidence-summary",
81443
81442
  "retrieve",
81444
81443
  "diagnose",
81445
81444
  "preflight",
@@ -81456,8 +81455,7 @@ var init_tool_policy = __esm(() => {
81456
81455
  "memory import",
81457
81456
  "memory migrate",
81458
81457
  "sync-plan",
81459
- "export",
81460
- "list-agents"
81458
+ "export"
81461
81459
  ];
81462
81460
  SWARM_COMMAND_TOOL_ALLOWLIST = new Set([
81463
81461
  "agents",
@@ -82979,9 +82977,11 @@ ${archBlock}`;
82979
82977
  if (!uiReview?.enabled) {
82980
82978
  prompt = prompt?.replace(", {{AGENT_PREFIX}}designer", "")?.replace(/\n 9\. \*\*UI\/UX DESIGN GATE\*\*:[\s\S]*?(?=\n10\. \*\*)/, `
82981
82979
  `)?.replace(`
82982
- {{AGENT_PREFIX}}designer - UI/UX design specs (scaffold generation for UI components — runs BEFORE coder on UI tasks)`, "")?.replace(/\n\{\{AGENT_PREFIX\}\}designer\nTASK: Design specification[\s\S]*?accessibility(?=\n\n## WORKFLOW)/, "")?.replace(`5a. **UI DESIGN GATE** (conditional Rule 9): If task matches UI trigger → {{AGENT_PREFIX}}designer produces scaffold → pass scaffold to coder as INPUT. If no match → skip.
82983
-
82984
- `, "")?.replace("→ After step 5a (or immediately if no UI task applies): Call update_task_status", "→ Call update_task_status")?.replace(" (if designer scaffold produced, include it as INPUT)", "");
82980
+ {{AGENT_PREFIX}}designer - UI/UX design specs (scaffold generation for UI components — runs BEFORE coder on UI tasks)`, "")?.replace(/\n\{\{AGENT_PREFIX\}\}designer\nTASK: Design specification[\s\S]*?(?=\n\n## WORKFLOW)/, "")?.replace(/, or designer/g, "")?.replace(`- the active swarm's designer agent = @{{AGENT_PREFIX}}designer
82981
+ `, "");
82982
+ if (/(?:@(?:\{\{AGENT_PREFIX\}\})?designer\b|\{\{AGENT_PREFIX\}\}designer\b)/i.test(prompt ?? "")) {
82983
+ console.warn("[swarm] WARNING: Custom architect prompt may still contain designer references after stripping. " + "Verify your custom prompt does not reference @designer when ui_review is disabled.");
82984
+ }
82985
82985
  }
82986
82986
  if (!designDocsEnabled) {
82987
82987
  prompt = prompt?.replace(", {{AGENT_PREFIX}}docs_design", "")?.replace(/### MODE: DESIGN_DOCS\n[\s\S]*?(?=### MODE: ISSUE_INGEST)/, "")?.replace(`- the active swarm's docs_design agent = @{{AGENT_PREFIX}}docs_design
@@ -83441,7 +83441,7 @@ the safe \`spec_write\` tool. Use it when:
83441
83441
  - requirements decomposition is non-trivial,
83442
83442
  - you would otherwise inline-author \`.swarm/spec.md\` yourself.
83443
83443
 
83444
- Continue handling small touch-ups (typos, cross-references) inline.
83444
+ Continue handling small touch-ups (typos, cross-references) via the spec_writer agent — the architect lacks the spec_write tool and must delegate all spec changes.
83445
83445
 
83446
83446
  ### ANTI-RATIONALIZATION
83447
83447
  - ✗ "The coder already knows these conventions" → Skills contain project-specific rules the model cannot know from training. Always pass.
@@ -83745,6 +83745,24 @@ HARD CONSTRAINTS (apply regardless of skill load success):
83745
83745
  - Do NOT touch .swarm/spec.md, CHANGELOG.md, or docs/releases/pending/* in this mode.
83746
83746
  - Requires design_docs.enabled: true — if the docs_design agent is not registered, instruct the user to enable it and stop.
83747
83747
 
83748
+ ### MODE: PR_REVIEW
83749
+ Activates when: architect receives \`[MODE: PR_REVIEW pr="https://github.com/..." council=true/false]\` signal from the pr-review command handler.
83750
+
83751
+ Purpose: Read-only structured PR review using parallel explorer lanes, independent reviewer validation, critic challenge, and synthesis. Does NOT mutate source code. Does NOT delegate to coder.
83752
+
83753
+ ACTION: Load skill file:.opencode/skills/swarm-pr-review/SKILL.md immediately and follow its protocol.
83754
+
83755
+ HARD CONSTRAINTS (apply regardless of skill load success):
83756
+ - Do NOT delegate to coder
83757
+ - Do NOT call declare_scope
83758
+ - Do NOT mutate source code
83759
+ - Do NOT create or modify files outside .swarm/
83760
+ - The orchestrator MUST NOT classify, confirm, disprove, or judge explorer candidates — validation is exclusively the reviewer's job
83761
+ - Explorers produce candidates only — reviewers verify or reject — critics challenge HIGH/CRITICAL and borderline findings
83762
+ - No finding may appear as CONFIRMED in the final report without reviewer validation provenance
83763
+ - Test execution, explorer lanes, reviewer dispatch, and critic challenge are all permitted within this mode
83764
+ - Quality is the only metric — time, tokens, and agent dispatches are irrelevant to correctness
83765
+
83748
83766
  ### MODE: ISSUE_INGEST
83749
83767
  Activates when the user invokes /swarm issue <url> or the architect receives an ISSUE_INGEST signal.
83750
83768
 
@@ -90942,11 +90960,11 @@ __export(exports_runtime, {
90942
90960
  _internals: () => _internals45
90943
90961
  });
90944
90962
  import * as path93 from "node:path";
90945
- import { fileURLToPath as fileURLToPath3 } from "node:url";
90963
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
90946
90964
  async function initTreeSitter() {
90947
90965
  if (!treeSitterInitPromise) {
90948
90966
  treeSitterInitPromise = (async () => {
90949
- const thisDir = path93.dirname(fileURLToPath3(import.meta.url));
90967
+ const thisDir = path93.dirname(fileURLToPath4(import.meta.url));
90950
90968
  const isSource = thisDir.replace(/\\/g, "/").endsWith("/src/lang");
90951
90969
  if (isSource) {
90952
90970
  await _internals45.parserInit();
@@ -90980,7 +90998,7 @@ function getWasmFileName(languageId) {
90980
90998
  return `tree-sitter-${sanitized}.wasm`;
90981
90999
  }
90982
91000
  function getGrammarsDirAbsolute() {
90983
- const thisDir = path93.dirname(fileURLToPath3(import.meta.url));
91001
+ const thisDir = path93.dirname(fileURLToPath4(import.meta.url));
90984
91002
  const normalized = thisDir.replace(/\\/g, "/");
90985
91003
  const isSource = normalized.endsWith("/src/lang");
90986
91004
  const isCliBundle = normalized.endsWith("/cli");
@@ -92278,11 +92296,11 @@ __export(exports_design_doc_drift, {
92278
92296
  runDesignDocDriftCheck: () => runDesignDocDriftCheck,
92279
92297
  _internals: () => _internals55
92280
92298
  });
92281
- import * as fs89 from "node:fs";
92282
- import * as path126 from "node:path";
92299
+ import * as fs94 from "node:fs";
92300
+ import * as path131 from "node:path";
92283
92301
  function mtimeMsOrNull(absPath) {
92284
92302
  try {
92285
- return fs89.statSync(absPath).mtimeMs;
92303
+ return fs94.statSync(absPath).mtimeMs;
92286
92304
  } catch {
92287
92305
  return null;
92288
92306
  }
@@ -92290,40 +92308,40 @@ function mtimeMsOrNull(absPath) {
92290
92308
  function resolveAnchorWithin(directory, anchor) {
92291
92309
  if (!anchor || typeof anchor !== "string")
92292
92310
  return null;
92293
- const root = path126.resolve(directory);
92294
- const resolved = path126.resolve(root, anchor);
92295
- const rel = path126.relative(root, resolved);
92296
- if (rel.startsWith("..") || path126.isAbsolute(rel))
92311
+ const root = path131.resolve(directory);
92312
+ const resolved = path131.resolve(root, anchor);
92313
+ const rel = path131.relative(root, resolved);
92314
+ if (rel.startsWith("..") || path131.isAbsolute(rel))
92297
92315
  return null;
92298
92316
  return resolved;
92299
92317
  }
92300
92318
  async function runDesignDocDriftCheck(directory, phase, outDir) {
92301
92319
  try {
92302
- const root = path126.resolve(directory);
92303
- const outAbs = path126.resolve(root, outDir);
92304
- const outRel = path126.relative(root, outAbs);
92305
- if (outRel.startsWith("..") || path126.isAbsolute(outRel)) {
92320
+ const root = path131.resolve(directory);
92321
+ const outAbs = path131.resolve(root, outDir);
92322
+ const outRel = path131.relative(root, outAbs);
92323
+ if (outRel.startsWith("..") || path131.isAbsolute(outRel)) {
92306
92324
  return null;
92307
92325
  }
92308
92326
  const docMtimes = new Map;
92309
92327
  const checkedDocs = [];
92310
92328
  const missingDocs = [];
92311
92329
  for (const [docName, relFile] of Object.entries(DESIGN_DOC_FILES)) {
92312
- const abs = path126.join(outAbs, relFile);
92330
+ const abs = path131.join(outAbs, relFile);
92313
92331
  const mtime = mtimeMsOrNull(abs);
92314
92332
  docMtimes.set(docName, mtime);
92315
92333
  if (mtime === null) {
92316
- missingDocs.push(path126.join(outDir, relFile));
92334
+ missingDocs.push(path131.join(outDir, relFile));
92317
92335
  } else {
92318
- checkedDocs.push(path126.join(outDir, relFile));
92336
+ checkedDocs.push(path131.join(outDir, relFile));
92319
92337
  }
92320
92338
  }
92321
- const traceabilityAbs = path126.join(outAbs, TRACEABILITY_REL);
92339
+ const traceabilityAbs = path131.join(outAbs, TRACEABILITY_REL);
92322
92340
  let registry3 = null;
92323
92341
  try {
92324
- const stat9 = await fs89.promises.stat(traceabilityAbs);
92342
+ const stat9 = await fs94.promises.stat(traceabilityAbs);
92325
92343
  if (stat9.size <= MAX_TRACEABILITY_BYTES) {
92326
- const raw = await fs89.promises.readFile(traceabilityAbs, "utf-8");
92344
+ const raw = await fs94.promises.readFile(traceabilityAbs, "utf-8");
92327
92345
  const parsed = JSON.parse(raw);
92328
92346
  registry3 = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
92329
92347
  }
@@ -92331,7 +92349,7 @@ async function runDesignDocDriftCheck(directory, phase, outDir) {
92331
92349
  registry3 = null;
92332
92350
  }
92333
92351
  const noDocs = checkedDocs.length === 0 || registry3 === null;
92334
- const specMtime = mtimeMsOrNull(path126.join(root, ".swarm", "spec.md"));
92352
+ const specMtime = mtimeMsOrNull(path131.join(root, ".swarm", "spec.md"));
92335
92353
  const staleSections = [];
92336
92354
  if (!noDocs && Array.isArray(registry3?.sections)) {
92337
92355
  for (const section of registry3.sections) {
@@ -92387,8 +92405,8 @@ async function runDesignDocDriftCheck(directory, phase, outDir) {
92387
92405
  };
92388
92406
  const filename = `${DOC_DRIFT_REPORT_PREFIX}${phase}.json`;
92389
92407
  const filePath = validateSwarmPath(directory, filename);
92390
- await fs89.promises.mkdir(path126.dirname(filePath), { recursive: true });
92391
- await fs89.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
92408
+ await fs94.promises.mkdir(path131.dirname(filePath), { recursive: true });
92409
+ await fs94.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
92392
92410
  getGlobalEventBus().publish("curator.docdrift.completed", {
92393
92411
  phase,
92394
92412
  verdict,
@@ -92418,10 +92436,10 @@ var init_design_doc_drift = __esm(() => {
92418
92436
  domain: "domain.md",
92419
92437
  "technical-spec": "technical-spec.md",
92420
92438
  "behavior-spec": "behavior-spec.md",
92421
- "reference-impl": path126.join("reference", "reference-impl.md"),
92422
- "idiom-notes": path126.join("reference", "idiom-notes.md")
92439
+ "reference-impl": path131.join("reference", "reference-impl.md"),
92440
+ "idiom-notes": path131.join("reference", "idiom-notes.md")
92423
92441
  };
92424
- TRACEABILITY_REL = path126.join("reference", "traceability.json");
92442
+ TRACEABILITY_REL = path131.join("reference", "traceability.json");
92425
92443
  _internals55 = {
92426
92444
  mtimeMsOrNull,
92427
92445
  resolveAnchorWithin,
@@ -92436,12 +92454,12 @@ __export(exports_project_context, {
92436
92454
  _internals: () => _internals71,
92437
92455
  LANG_BACKEND_DETECTION_TIMEOUT_MS: () => LANG_BACKEND_DETECTION_TIMEOUT_MS
92438
92456
  });
92439
- import * as fs118 from "node:fs";
92440
- import * as path161 from "node:path";
92457
+ import * as fs123 from "node:fs";
92458
+ import * as path166 from "node:path";
92441
92459
  function detectFileExists2(directory, pattern) {
92442
92460
  if (pattern.includes("*") || pattern.includes("?")) {
92443
92461
  try {
92444
- const files = fs118.readdirSync(directory);
92462
+ const files = fs123.readdirSync(directory);
92445
92463
  const regex = new RegExp(`^${pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".")}$`);
92446
92464
  return files.some((f) => regex.test(f));
92447
92465
  } catch {
@@ -92449,7 +92467,7 @@ function detectFileExists2(directory, pattern) {
92449
92467
  }
92450
92468
  }
92451
92469
  try {
92452
- fs118.accessSync(path161.join(directory, pattern));
92470
+ fs123.accessSync(path166.join(directory, pattern));
92453
92471
  return true;
92454
92472
  } catch {
92455
92473
  return false;
@@ -92458,7 +92476,7 @@ function detectFileExists2(directory, pattern) {
92458
92476
  function selectTestCommandFromScriptsTest(backend, directory) {
92459
92477
  let pkgRaw;
92460
92478
  try {
92461
- pkgRaw = fs118.readFileSync(path161.join(directory, "package.json"), "utf-8");
92479
+ pkgRaw = fs123.readFileSync(path166.join(directory, "package.json"), "utf-8");
92462
92480
  } catch {
92463
92481
  return null;
92464
92482
  }
@@ -92567,7 +92585,7 @@ var init_project_context = __esm(() => {
92567
92585
  init_package();
92568
92586
  init_agents2();
92569
92587
  init_critic();
92570
- import * as path162 from "node:path";
92588
+ import * as path167 from "node:path";
92571
92589
 
92572
92590
  // src/background/index.ts
92573
92591
  init_event_bus();
@@ -111461,10 +111479,9 @@ init_lint();
111461
111479
  init_zod();
111462
111480
  init_config();
111463
111481
  init_schema();
111464
- init_qa_gate_profile();
111465
111482
  init_manager2();
111466
- import * as fs90 from "node:fs";
111467
- import * as path127 from "node:path";
111483
+ import * as fs95 from "node:fs";
111484
+ import * as path132 from "node:path";
111468
111485
 
111469
111486
  // src/full-auto/phase-approval.ts
111470
111487
  init_utils2();
@@ -111643,7 +111660,6 @@ init_ledger();
111643
111660
  init_manager();
111644
111661
  init_snapshot_writer();
111645
111662
  init_state();
111646
- init_store();
111647
111663
  init_telemetry();
111648
111664
 
111649
111665
  // src/turbo/lean/phase-ready.ts
@@ -112137,12 +112153,765 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
112137
112153
  // src/tools/phase-complete.ts
112138
112154
  init_logger();
112139
112155
  init_create_tool();
112156
+
112157
+ // src/tools/phase-complete/gates/architecture-supervisor-gate.ts
112158
+ init_store();
112159
+ async function runArchitectureSupervisorGate(ctx) {
112160
+ const { phase, dir, pluginConfig, agentsDispatched, safeWarn } = ctx;
112161
+ const asConfig = pluginConfig.architectural_supervision;
112162
+ const summarizeFindings = (findings) => {
112163
+ if (!Array.isArray(findings) || findings.length === 0)
112164
+ return "";
112165
+ const details = findings.map((f) => f && typeof f === "object" && typeof f.description === "string" ? f.description : undefined).filter((d) => Boolean(d));
112166
+ return details.length > 0 ? `
112167
+ Findings: ${details.join("; ")}` : "";
112168
+ };
112169
+ const asBlocked = (reason, message) => ({
112170
+ blocked: true,
112171
+ reason,
112172
+ message,
112173
+ agentsDispatched,
112174
+ agentsMissing: [],
112175
+ warnings: []
112176
+ });
112177
+ let asEntry = null;
112178
+ try {
112179
+ asEntry = readSupervisorReportRaw(dir, phase);
112180
+ } catch (asError) {
112181
+ return asBlocked("ARCH_SUPERVISOR_ERROR", `Phase ${phase} cannot be completed: architecture supervisor gate encountered an error. Error: ${String(asError)}`);
112182
+ }
112183
+ if (!asEntry) {
112184
+ return asBlocked("ARCH_SUPERVISOR_REQUIRED", `Phase ${phase} cannot be completed: architectural_supervision gate mode is enabled and no architecture supervisor evidence was found at .swarm/evidence/${phase}/architecture-supervisor.json. Dispatch critic_architecture_supervisor with the phase + agent summaries, then call write_architecture_supervisor_evidence.`);
112185
+ }
112186
+ const now = new Date;
112187
+ const asTime = asEntry.timestamp ? new Date(asEntry.timestamp) : null;
112188
+ if (!asTime || Number.isNaN(asTime.getTime())) {
112189
+ return asBlocked("ARCH_SUPERVISOR_INVALID_TIMESTAMP", `Phase ${phase} cannot be completed: architecture supervisor evidence has a missing or invalid timestamp.`);
112190
+ }
112191
+ if (asTime.getTime() > now.getTime()) {
112192
+ return asBlocked("ARCH_SUPERVISOR_FUTURE_TIMESTAMP", `Phase ${phase} cannot be completed: architecture supervisor evidence timestamp is in the future.`);
112193
+ }
112194
+ if (now.getTime() - asTime.getTime() > 24 * 60 * 60 * 1000) {
112195
+ return asBlocked("ARCH_SUPERVISOR_STALE_EVIDENCE", `Phase ${phase} cannot be completed: architecture supervisor evidence is older than 24 hours. Re-run the supervisor for fresh review.`);
112196
+ }
112197
+ if (typeof asEntry.phase_number !== "number" || asEntry.phase_number !== phase) {
112198
+ return asBlocked("ARCH_SUPERVISOR_PHASE_MISMATCH", `Phase ${phase} cannot be completed: architecture supervisor evidence is for phase ${String(asEntry.phase_number)}, not phase ${phase}.`);
112199
+ }
112200
+ const asVerdict = asEntry.verdict;
112201
+ if (asVerdict === "REJECT") {
112202
+ return asBlocked("ARCH_SUPERVISOR_REJECTED", `Phase ${phase} cannot be completed: architecture supervisor returned verdict 'REJECT'. Address the system-level findings before completing the phase.${summarizeFindings(asEntry.findings)}`);
112203
+ }
112204
+ if (asVerdict === "CONCERNS") {
112205
+ if (asConfig?.allow_concerns_to_complete === false) {
112206
+ return asBlocked("ARCH_SUPERVISOR_CONCERNS", `Phase ${phase} cannot be completed: architecture supervisor returned verdict 'CONCERNS' and allow_concerns_to_complete is disabled.${summarizeFindings(asEntry.findings)}`);
112207
+ }
112208
+ safeWarn(`[phase_complete] Architecture supervisor returned CONCERNS for phase ${phase} — proceeding (allow_concerns_to_complete is enabled)`, undefined);
112209
+ } else if (asVerdict !== "APPROVE") {
112210
+ return asBlocked("ARCH_SUPERVISOR_INVALID", `Phase ${phase} cannot be completed: architecture supervisor evidence contains unrecognized verdict '${String(asVerdict)}'. Expected one of: APPROVE, CONCERNS, REJECT.`);
112211
+ }
112212
+ return { blocked: false, agentsDispatched, agentsMissing: [], warnings: [] };
112213
+ }
112214
+ // src/tools/phase-complete/gates/completion-verify-gate.ts
112215
+ async function runCompletionVerifyGate(ctx) {
112216
+ const { phase, dir, agentsDispatched, safeWarn } = ctx;
112217
+ try {
112218
+ const completionResultRaw = await executeCompletionVerify({ phase }, dir);
112219
+ const completionResult = JSON.parse(completionResultRaw);
112220
+ if (completionResult.status === "blocked") {
112221
+ return {
112222
+ blocked: true,
112223
+ reason: "COMPLETION_INCOMPLETE",
112224
+ message: `Phase ${phase} cannot be completed: ${completionResult.reason}`,
112225
+ agentsDispatched,
112226
+ agentsMissing: [],
112227
+ warnings: completionResult.blockedTasks ? [
112228
+ `Blocked tasks: ${completionResult.blockedTasks.map((t) => t.task_id).join(", ")}`
112229
+ ] : []
112230
+ };
112231
+ }
112232
+ return {
112233
+ blocked: false,
112234
+ agentsDispatched,
112235
+ agentsMissing: [],
112236
+ warnings: []
112237
+ };
112238
+ } catch (completionError) {
112239
+ safeWarn(`[phase_complete] Completion verify error (non-blocking):`, completionError);
112240
+ return {
112241
+ blocked: false,
112242
+ agentsDispatched,
112243
+ agentsMissing: [],
112244
+ warnings: []
112245
+ };
112246
+ }
112247
+ }
112248
+ // src/tools/phase-complete/gates/drift-gate.ts
112249
+ init_qa_gate_profile();
112250
+ init_manager();
112251
+ init_state();
112252
+ import * as fs89 from "node:fs";
112253
+ import * as path126 from "node:path";
112254
+ async function runDriftGate(ctx) {
112255
+ const { phase, dir, sessionID, agentsDispatched, safeWarn } = ctx;
112256
+ let driftCheckEnabled = true;
112257
+ let driftHasSpecMd = false;
112258
+ try {
112259
+ const specMdPath = path126.join(dir, ".swarm", "spec.md");
112260
+ driftHasSpecMd = fs89.existsSync(specMdPath);
112261
+ const gatePlan = await loadPlan(dir);
112262
+ if (gatePlan) {
112263
+ const gatePlanId = derivePlanId(gatePlan);
112264
+ const gateProfile = getProfile(dir, gatePlanId);
112265
+ if (gateProfile) {
112266
+ const gateSession = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
112267
+ const gateOverrides = gateSession?.qaGateSessionOverrides ?? {};
112268
+ const gateEffective = getEffectiveGates(gateProfile, gateOverrides);
112269
+ driftCheckEnabled = gateEffective.drift_check === true;
112270
+ }
112271
+ }
112272
+ } catch (gateLoadError) {
112273
+ safeWarn(`[phase_complete] QA gate profile load error, drift_check defaults to enabled:`, gateLoadError);
112274
+ }
112275
+ if (!driftCheckEnabled) {
112276
+ return {
112277
+ blocked: false,
112278
+ agentsDispatched,
112279
+ agentsMissing: [],
112280
+ warnings: [
112281
+ `drift_check gate is disabled. Drift verification was skipped for phase ${phase}.`
112282
+ ]
112283
+ };
112284
+ }
112285
+ let phaseType;
112286
+ try {
112287
+ const planPath = path126.join(dir, ".swarm", "plan.json");
112288
+ if (fs89.existsSync(planPath)) {
112289
+ const planRaw = fs89.readFileSync(planPath, "utf-8");
112290
+ const plan = JSON.parse(planRaw);
112291
+ const targetPhase = plan.phases?.find((p) => p.id === phase);
112292
+ phaseType = targetPhase?.type;
112293
+ }
112294
+ } catch {}
112295
+ if (phaseType === "non-code") {
112296
+ return {
112297
+ blocked: false,
112298
+ agentsDispatched,
112299
+ agentsMissing: [],
112300
+ warnings: [
112301
+ `Phase ${phase} is annotated as 'non-code'. Drift verification was skipped per phase type annotation.`
112302
+ ]
112303
+ };
112304
+ }
112305
+ try {
112306
+ const driftEvidencePath = path126.join(dir, ".swarm", "evidence", String(phase), "drift-verifier.json");
112307
+ let driftVerdictFound = false;
112308
+ let driftVerdictApproved = false;
112309
+ try {
112310
+ const driftEvidenceContent = fs89.readFileSync(driftEvidencePath, "utf-8");
112311
+ const driftEvidence = JSON.parse(driftEvidenceContent);
112312
+ const entries = driftEvidence.entries ?? [];
112313
+ for (const entry of entries) {
112314
+ if (typeof entry.type === "string" && entry.type.includes("drift") && typeof entry.verdict === "string") {
112315
+ driftVerdictFound = true;
112316
+ if (entry.verdict === "approved") {
112317
+ driftVerdictApproved = true;
112318
+ }
112319
+ if (entry.verdict === "rejected" || typeof entry.summary === "string" && entry.summary.includes("NEEDS_REVISION")) {
112320
+ return {
112321
+ blocked: true,
112322
+ reason: "DRIFT_VERIFICATION_REJECTED",
112323
+ message: `Phase ${phase} cannot be completed: drift verifier returned verdict '${entry.verdict}'. Address the drift issues before completing the phase.`,
112324
+ agentsDispatched,
112325
+ agentsMissing: [],
112326
+ warnings: []
112327
+ };
112328
+ }
112329
+ }
112330
+ }
112331
+ } catch (readError) {
112332
+ if (readError.code !== "ENOENT") {
112333
+ safeWarn(`[phase_complete] Drift verifier evidence unreadable:`, readError);
112334
+ }
112335
+ driftVerdictFound = false;
112336
+ }
112337
+ if (!driftVerdictFound) {
112338
+ if (!driftHasSpecMd) {
112339
+ let incompleteTaskCount = 0;
112340
+ let planParseable = false;
112341
+ try {
112342
+ const planPath = path126.join(dir, ".swarm", "plan.json");
112343
+ if (fs89.existsSync(planPath)) {
112344
+ const planRaw = fs89.readFileSync(planPath, "utf-8");
112345
+ const plan = JSON.parse(planRaw);
112346
+ planParseable = true;
112347
+ const planPhase = plan.phases?.find((p) => p.id === phase);
112348
+ if (planPhase?.tasks) {
112349
+ incompleteTaskCount = planPhase.tasks.filter((t) => t.status !== "completed" && t.status !== "closed").length;
112350
+ }
112351
+ }
112352
+ } catch {}
112353
+ if (!planParseable) {
112354
+ return {
112355
+ blocked: false,
112356
+ agentsDispatched,
112357
+ agentsMissing: [],
112358
+ warnings: [
112359
+ `No spec.md found and drift verification evidence missing — consider running critic_drift_verifier before phase completion.`
112360
+ ]
112361
+ };
112362
+ } else if (incompleteTaskCount > 0) {
112363
+ return {
112364
+ blocked: false,
112365
+ agentsDispatched,
112366
+ agentsMissing: [],
112367
+ warnings: [
112368
+ `No spec.md found and drift verification evidence missing. Phase ${phase} has ${incompleteTaskCount} incomplete task(s) in plan.json — consider running critic_drift_verifier before phase completion.`
112369
+ ]
112370
+ };
112371
+ } else {
112372
+ return {
112373
+ blocked: false,
112374
+ agentsDispatched,
112375
+ agentsMissing: [],
112376
+ warnings: [
112377
+ `No spec.md found. Phase ${phase} tasks are all completed in plan.json. Drift verification was skipped.`
112378
+ ]
112379
+ };
112380
+ }
112381
+ } else {
112382
+ return {
112383
+ blocked: true,
112384
+ reason: "DRIFT_VERIFICATION_MISSING",
112385
+ message: `Phase ${phase} cannot be completed: drift_check is enabled and drift verifier evidence not found at .swarm/evidence/${phase}/drift-verifier.json. Run drift verification before completing the phase.`,
112386
+ agentsDispatched,
112387
+ agentsMissing: [],
112388
+ warnings: []
112389
+ };
112390
+ }
112391
+ }
112392
+ if (!driftVerdictApproved && driftVerdictFound) {
112393
+ return {
112394
+ blocked: true,
112395
+ reason: "DRIFT_VERIFICATION_REJECTED",
112396
+ message: `Phase ${phase} cannot be completed: drift verifier verdict is not approved.`,
112397
+ agentsDispatched,
112398
+ agentsMissing: [],
112399
+ warnings: []
112400
+ };
112401
+ }
112402
+ return {
112403
+ blocked: false,
112404
+ agentsDispatched,
112405
+ agentsMissing: [],
112406
+ warnings: []
112407
+ };
112408
+ } catch (driftError) {
112409
+ return {
112410
+ blocked: true,
112411
+ reason: "DRIFT_VERIFICATION_ERROR",
112412
+ message: `Phase ${phase} cannot be completed: drift verification encountered an error: ${driftError instanceof Error ? driftError.message : String(driftError)}. This is a hard block — resolve the error before completing the phase.`,
112413
+ agentsDispatched,
112414
+ agentsMissing: [],
112415
+ warnings: []
112416
+ };
112417
+ }
112418
+ }
112419
+ // src/tools/phase-complete/gates/final-council-gate.ts
112420
+ init_qa_gate_profile();
112421
+ init_manager();
112422
+ init_state();
112423
+ import * as fs90 from "node:fs";
112424
+ import * as path127 from "node:path";
112425
+ async function runFinalCouncilGate(ctx) {
112426
+ const { phase, dir, sessionID, agentsDispatched, safeWarn } = ctx;
112427
+ let finalCouncilEnabled = false;
112428
+ try {
112429
+ const plan = await loadPlan(dir);
112430
+ if (plan) {
112431
+ const lastPhaseId = plan.phases[plan.phases.length - 1]?.id;
112432
+ if (lastPhaseId !== undefined && phase === lastPhaseId) {
112433
+ const planId = derivePlanId(plan);
112434
+ const profile = getProfile(dir, planId);
112435
+ if (profile) {
112436
+ const session = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
112437
+ const overrides = session?.qaGateSessionOverrides ?? {};
112438
+ const effective = getEffectiveGates(profile, overrides);
112439
+ if (effective.final_council === true) {
112440
+ finalCouncilEnabled = true;
112441
+ const fcPath = path127.join(dir, ".swarm", "evidence", "final-council.json");
112442
+ let fcVerdictFound = false;
112443
+ let _fcVerdict;
112444
+ try {
112445
+ const fcContent = fs90.readFileSync(fcPath, "utf-8");
112446
+ const fcBundle = JSON.parse(fcContent);
112447
+ for (const entry of fcBundle.entries ?? []) {
112448
+ if (typeof entry.type === "string" && entry.type === "final-council" && typeof entry.verdict === "string") {
112449
+ fcVerdictFound = true;
112450
+ _fcVerdict = entry.verdict;
112451
+ if (plan) {
112452
+ const currentPlanId = derivePlanId(plan);
112453
+ if (entry.plan_id && entry.plan_id !== currentPlanId) {
112454
+ return {
112455
+ blocked: true,
112456
+ reason: "final_council_plan_mismatch",
112457
+ message: `Final council evidence belongs to a different plan (evidence: ${entry.plan_id}, current: ${currentPlanId}). Re-run the final council.`,
112458
+ agentsDispatched,
112459
+ agentsMissing: [],
112460
+ warnings: []
112461
+ };
112462
+ }
112463
+ if (!entry.plan_id) {
112464
+ return {
112465
+ blocked: true,
112466
+ reason: "FINAL_COUNCIL_PLAN_ID_REQUIRED",
112467
+ message: `Phase ${phase} (last phase) cannot be completed: final council evidence is missing plan_id binding. Re-run the final council to generate evidence with plan identity.`,
112468
+ agentsDispatched,
112469
+ agentsMissing: [],
112470
+ warnings: []
112471
+ };
112472
+ }
112473
+ }
112474
+ if (typeof entry.quorumSize !== "number" || !Number.isFinite(entry.quorumSize) || entry.quorumSize < 5) {
112475
+ return {
112476
+ blocked: true,
112477
+ reason: "FINAL_COUNCIL_MISSING_QUORUM",
112478
+ 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.`,
112479
+ agentsDispatched,
112480
+ agentsMissing: [],
112481
+ warnings: []
112482
+ };
112483
+ }
112484
+ const requiredFinalCouncilMembers = [
112485
+ "critic",
112486
+ "reviewer",
112487
+ "sme",
112488
+ "test_engineer",
112489
+ "explorer"
112490
+ ];
112491
+ const membersVoted = Array.isArray(entry.membersVoted) ? entry.membersVoted.filter((member) => typeof member === "string") : [];
112492
+ const membersAbsent = Array.isArray(entry.membersAbsent) ? entry.membersAbsent.filter((member) => typeof member === "string") : [];
112493
+ const distinctMembersVoted = new Set(membersVoted);
112494
+ const hasAllRequiredMembers = requiredFinalCouncilMembers.every((member) => distinctMembersVoted.has(member)) && distinctMembersVoted.size === requiredFinalCouncilMembers.length && membersAbsent.length === 0;
112495
+ if (!hasAllRequiredMembers) {
112496
+ return {
112497
+ blocked: true,
112498
+ reason: "FINAL_COUNCIL_MISSING_QUORUM",
112499
+ 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.`,
112500
+ agentsDispatched,
112501
+ agentsMissing: [],
112502
+ warnings: []
112503
+ };
112504
+ }
112505
+ if (entry.verdict === "rejected" || entry.verdict === "REJECTED") {
112506
+ return {
112507
+ blocked: true,
112508
+ reason: "FINAL_COUNCIL_REJECTED",
112509
+ message: `Phase ${phase} (last phase) cannot be completed: final council returned verdict 'REJECTED'. Address the required fixes before completing the project.`,
112510
+ agentsDispatched,
112511
+ agentsMissing: [],
112512
+ warnings: []
112513
+ };
112514
+ }
112515
+ if (entry.verdict !== "approved" && entry.verdict !== "APPROVED") {
112516
+ return {
112517
+ blocked: true,
112518
+ reason: "FINAL_COUNCIL_INVALID_VERDICT",
112519
+ message: `Phase ${phase} (last phase) cannot be completed: final council evidence contains unrecognized verdict '${entry.verdict}'. Expected 'approved'.`,
112520
+ agentsDispatched,
112521
+ agentsMissing: [],
112522
+ warnings: []
112523
+ };
112524
+ }
112525
+ }
112526
+ }
112527
+ } catch (readErr) {
112528
+ if (readErr.code !== "ENOENT") {
112529
+ safeWarn(`[phase_complete] Final council evidence unreadable:`, readErr);
112530
+ }
112531
+ fcVerdictFound = false;
112532
+ }
112533
+ if (!fcVerdictFound) {
112534
+ return {
112535
+ blocked: true,
112536
+ reason: "FINAL_COUNCIL_REQUIRED",
112537
+ final_council_required: true,
112538
+ 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.`,
112539
+ agentsDispatched,
112540
+ agentsMissing: [],
112541
+ warnings: [
112542
+ `Final council required - dispatch the five project-scoped council members, then call write_final_council_evidence to persist quorumed evidence.`
112543
+ ]
112544
+ };
112545
+ }
112546
+ }
112547
+ }
112548
+ }
112549
+ }
112550
+ } catch (fcError) {
112551
+ if (finalCouncilEnabled) {
112552
+ return {
112553
+ blocked: true,
112554
+ reason: "FINAL_COUNCIL_ERROR",
112555
+ message: `Phase ${phase} (last phase) cannot be completed: final council gate encountered an error. Error: ${String(fcError)}`,
112556
+ agentsDispatched,
112557
+ agentsMissing: [],
112558
+ warnings: [`FINAL_COUNCIL_ERROR: ${String(fcError)}`]
112559
+ };
112560
+ } else {
112561
+ safeWarn(`[phase_complete] Final council gate error (non-blocking):`, fcError);
112562
+ }
112563
+ }
112564
+ return { blocked: false, agentsDispatched, agentsMissing: [], warnings: [] };
112565
+ }
112566
+ // src/tools/phase-complete/gates/hallucination-gate.ts
112567
+ init_qa_gate_profile();
112568
+ init_manager();
112569
+ init_state();
112570
+ import * as fs91 from "node:fs";
112571
+ import * as path128 from "node:path";
112572
+ async function runHallucinationGate(ctx) {
112573
+ const { phase, dir, sessionID, agentsDispatched, safeWarn } = ctx;
112574
+ try {
112575
+ const plan = await loadPlan(dir);
112576
+ if (plan) {
112577
+ const planId = derivePlanId(plan);
112578
+ const profile = getProfile(dir, planId);
112579
+ if (profile) {
112580
+ const session = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
112581
+ const overrides = session?.qaGateSessionOverrides ?? {};
112582
+ const effective = getEffectiveGates(profile, overrides);
112583
+ if (effective.hallucination_guard === true) {
112584
+ const hgPath = path128.join(dir, ".swarm", "evidence", String(phase), "hallucination-guard.json");
112585
+ let hgVerdictFound = false;
112586
+ let hgVerdictApproved = false;
112587
+ try {
112588
+ const hgContent = fs91.readFileSync(hgPath, "utf-8");
112589
+ const hgBundle = JSON.parse(hgContent);
112590
+ for (const entry of hgBundle.entries ?? []) {
112591
+ if (typeof entry.type === "string" && entry.type.includes("hallucination") && typeof entry.verdict === "string") {
112592
+ hgVerdictFound = true;
112593
+ if (entry.verdict === "approved") {
112594
+ hgVerdictApproved = true;
112595
+ }
112596
+ if (entry.verdict === "rejected" || typeof entry.summary === "string" && entry.summary.includes("NEEDS_REVISION")) {
112597
+ return {
112598
+ blocked: true,
112599
+ reason: "HALLUCINATION_VERIFICATION_REJECTED",
112600
+ message: `Phase ${phase} cannot be completed: hallucination verifier returned verdict '${entry.verdict}'. Remove fabricated APIs/signatures and fix broken citations before completing the phase.`,
112601
+ agentsDispatched,
112602
+ agentsMissing: [],
112603
+ warnings: []
112604
+ };
112605
+ }
112606
+ }
112607
+ }
112608
+ } catch (readErr) {
112609
+ if (readErr.code !== "ENOENT") {
112610
+ safeWarn(`[phase_complete] Hallucination guard evidence unreadable:`, readErr);
112611
+ }
112612
+ hgVerdictFound = false;
112613
+ }
112614
+ if (!hgVerdictFound) {
112615
+ return {
112616
+ blocked: true,
112617
+ reason: "HALLUCINATION_VERIFICATION_MISSING",
112618
+ message: `Phase ${phase} cannot be completed: hallucination_guard is enabled and evidence not found at .swarm/evidence/${phase}/hallucination-guard.json. Delegate to critic_hallucination_verifier and call write_hallucination_evidence before completing the phase.`,
112619
+ agentsDispatched,
112620
+ agentsMissing: [],
112621
+ warnings: []
112622
+ };
112623
+ }
112624
+ if (!hgVerdictApproved) {
112625
+ return {
112626
+ blocked: true,
112627
+ reason: "HALLUCINATION_VERIFICATION_REJECTED",
112628
+ message: `Phase ${phase} cannot be completed: hallucination verifier verdict is not approved.`,
112629
+ agentsDispatched,
112630
+ agentsMissing: [],
112631
+ warnings: []
112632
+ };
112633
+ }
112634
+ }
112635
+ }
112636
+ }
112637
+ } catch (hgError) {
112638
+ safeWarn(`[phase_complete] Hallucination guard error (non-blocking):`, hgError);
112639
+ }
112640
+ return { blocked: false, agentsDispatched, agentsMissing: [], warnings: [] };
112641
+ }
112642
+ // src/tools/phase-complete/gates/mutation-gate.ts
112643
+ init_qa_gate_profile();
112644
+ init_manager();
112645
+ init_state();
112646
+ import * as fs92 from "node:fs";
112647
+ import * as path129 from "node:path";
112648
+ async function runMutationGate(ctx) {
112649
+ const { phase, dir, sessionID, agentsDispatched, safeWarn } = ctx;
112650
+ try {
112651
+ const plan = await loadPlan(dir);
112652
+ if (plan) {
112653
+ const planId = derivePlanId(plan);
112654
+ const profile = getProfile(dir, planId);
112655
+ if (profile) {
112656
+ const session = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
112657
+ const overrides = session?.qaGateSessionOverrides ?? {};
112658
+ const effective = getEffectiveGates(profile, overrides);
112659
+ if (effective.mutation_test === true) {
112660
+ const mgPath = path129.join(dir, ".swarm", "evidence", String(phase), "mutation-gate.json");
112661
+ let mgVerdictFound = false;
112662
+ let mgVerdict;
112663
+ try {
112664
+ const mgContent = fs92.readFileSync(mgPath, "utf-8");
112665
+ const mgBundle = JSON.parse(mgContent);
112666
+ for (const entry of mgBundle.entries ?? []) {
112667
+ if (typeof entry.type === "string" && entry.type === "mutation-gate" && typeof entry.verdict === "string") {
112668
+ mgVerdictFound = true;
112669
+ mgVerdict = entry.verdict;
112670
+ if (entry.verdict === "fail") {
112671
+ return {
112672
+ blocked: true,
112673
+ reason: "MUTATION_GATE_FAIL",
112674
+ message: `Phase ${phase} cannot be completed: mutation gate returned verdict 'fail'. Resolve surviving mutants or lower the kill-rate threshold before completing the phase.`,
112675
+ agentsDispatched,
112676
+ agentsMissing: [],
112677
+ warnings: []
112678
+ };
112679
+ } else if (!["pass", "warn", "skip"].includes(entry.verdict)) {
112680
+ return {
112681
+ blocked: true,
112682
+ reason: "MUTATION_GATE_FAIL",
112683
+ message: `Phase ${phase} cannot be completed: mutation gate evidence contains unrecognized verdict '${entry.verdict}'. Expected one of: pass, warn, fail, skip.`,
112684
+ agentsDispatched,
112685
+ agentsMissing: [],
112686
+ warnings: []
112687
+ };
112688
+ }
112689
+ }
112690
+ }
112691
+ } catch (readErr) {
112692
+ if (readErr.code !== "ENOENT") {
112693
+ safeWarn(`[phase_complete] Mutation gate evidence unreadable:`, readErr);
112694
+ }
112695
+ mgVerdictFound = false;
112696
+ }
112697
+ if (!mgVerdictFound) {
112698
+ return {
112699
+ blocked: true,
112700
+ reason: "MUTATION_GATE_MISSING",
112701
+ message: `Phase ${phase} cannot be completed: mutation_test is enabled and evidence not found at .swarm/evidence/${phase}/mutation-gate.json. Run mutation_test, then call write_mutation_evidence before completing the phase.`,
112702
+ agentsDispatched,
112703
+ agentsMissing: [],
112704
+ warnings: []
112705
+ };
112706
+ }
112707
+ if (mgVerdict === "warn") {
112708
+ safeWarn(`[phase_complete] Mutation gate verdict is 'warn' for phase ${phase} — proceeding with warning`, undefined);
112709
+ }
112710
+ }
112711
+ }
112712
+ }
112713
+ } catch (mgError) {
112714
+ safeWarn(`[phase_complete] Mutation gate error (non-blocking):`, mgError);
112715
+ }
112716
+ return { blocked: false, agentsDispatched, agentsMissing: [], warnings: [] };
112717
+ }
112718
+ // src/tools/phase-complete/gates/phase-council-gate.ts
112719
+ init_qa_gate_profile();
112720
+ init_manager();
112721
+ init_state();
112722
+ import * as fs93 from "node:fs";
112723
+ import * as path130 from "node:path";
112724
+ async function runPhaseCouncilGate(ctx) {
112725
+ const { phase, dir, sessionID, pluginConfig, agentsDispatched, safeWarn } = ctx;
112726
+ let councilModeEnabled = false;
112727
+ try {
112728
+ const plan = await loadPlan(dir);
112729
+ if (plan) {
112730
+ const planId = derivePlanId(plan);
112731
+ const profile = getProfile(dir, planId);
112732
+ if (profile) {
112733
+ const session = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
112734
+ const overrides = session?.qaGateSessionOverrides ?? {};
112735
+ const effective = getEffectiveGates(profile, overrides);
112736
+ if (effective.council_mode === true) {
112737
+ councilModeEnabled = true;
112738
+ const pcPath = path130.join(dir, ".swarm", "evidence", String(phase), "phase-council.json");
112739
+ let pcVerdictFound = false;
112740
+ let _pcVerdict;
112741
+ let pcQuorumSize;
112742
+ let pcTimestamp;
112743
+ let pcPhaseNumber;
112744
+ try {
112745
+ const pcContent = fs93.readFileSync(pcPath, "utf-8");
112746
+ const pcBundle = JSON.parse(pcContent);
112747
+ for (const entry of pcBundle.entries ?? []) {
112748
+ if (typeof entry.type === "string" && entry.type === "phase-council" && typeof entry.verdict === "string") {
112749
+ pcVerdictFound = true;
112750
+ _pcVerdict = entry.verdict;
112751
+ pcQuorumSize = typeof entry.quorumSize === "number" ? entry.quorumSize : undefined;
112752
+ pcTimestamp = typeof entry.timestamp === "string" ? entry.timestamp : undefined;
112753
+ pcPhaseNumber = typeof entry.phase_number === "number" ? entry.phase_number : typeof entry.phase === "number" ? entry.phase : undefined;
112754
+ const now = new Date;
112755
+ const pcTime = pcTimestamp ? new Date(pcTimestamp) : null;
112756
+ if (!pcTime || Number.isNaN(pcTime.getTime())) {
112757
+ return {
112758
+ blocked: true,
112759
+ reason: "PHASE_COUNCIL_INVALID_TIMESTAMP",
112760
+ message: `Phase ${phase} cannot be completed: phase council evidence has missing or invalid timestamp.`,
112761
+ agentsDispatched,
112762
+ agentsMissing: [],
112763
+ warnings: []
112764
+ };
112765
+ }
112766
+ const maxAge = 24 * 60 * 60 * 1000;
112767
+ if (pcTime.getTime() > now.getTime()) {
112768
+ return {
112769
+ blocked: true,
112770
+ reason: "PHASE_COUNCIL_FUTURE_TIMESTAMP",
112771
+ message: `Phase ${phase} cannot be completed: phase council evidence timestamp is in the future.`,
112772
+ agentsDispatched,
112773
+ agentsMissing: [],
112774
+ warnings: []
112775
+ };
112776
+ }
112777
+ if (now.getTime() - pcTime.getTime() > maxAge) {
112778
+ return {
112779
+ blocked: true,
112780
+ reason: "PHASE_COUNCIL_STALE_EVIDENCE",
112781
+ message: `Phase ${phase} cannot be completed: phase council evidence is older than 24 hours. Re-convene council for fresh review.`,
112782
+ agentsDispatched,
112783
+ agentsMissing: [],
112784
+ warnings: []
112785
+ };
112786
+ }
112787
+ if (entry.verdict === "REJECT" || entry.verdict === "reject") {
112788
+ const requiredFixes = entry.requiredFixes ?? entry.required_fixes ?? [];
112789
+ const fixesDetail = Array.isArray(requiredFixes) && requiredFixes.length > 0 ? `
112790
+ Required fixes: ${requiredFixes.map((f) => f.detail ?? JSON.stringify(f)).join("; ")}` : "";
112791
+ return {
112792
+ blocked: true,
112793
+ reason: "PHASE_COUNCIL_REJECTED",
112794
+ message: `Phase ${phase} cannot be completed: phase council returned verdict 'REJECT'. Address the required fixes before completing the phase.${fixesDetail}`,
112795
+ agentsDispatched,
112796
+ agentsMissing: [],
112797
+ warnings: []
112798
+ };
112799
+ }
112800
+ if (entry.verdict === "CONCERNS" || entry.verdict === "concerns") {
112801
+ const phaseConcernsAllow = pluginConfig.council?.phaseConcernsAllowComplete ?? true;
112802
+ if (!phaseConcernsAllow) {
112803
+ const advisoryNotes = entry.advisoryNotes ?? entry.advisory_notes ?? [];
112804
+ const notesDetail = Array.isArray(advisoryNotes) && advisoryNotes.length > 0 ? `
112805
+ Advisory notes: ${advisoryNotes.join("; ")}` : "";
112806
+ return {
112807
+ blocked: true,
112808
+ reason: "PHASE_COUNCIL_CONCERNS",
112809
+ message: `Phase ${phase} cannot be completed: phase council returned verdict 'CONCERNS'.${notesDetail}`,
112810
+ agentsDispatched,
112811
+ agentsMissing: [],
112812
+ warnings: []
112813
+ };
112814
+ }
112815
+ safeWarn(`[phase_complete] Phase council returned CONCERNS for phase ${phase} — proceeding (phaseConcernsAllowComplete is enabled)`, undefined);
112816
+ }
112817
+ if (entry.verdict !== "APPROVE" && entry.verdict !== "approve" && entry.verdict !== "CONCERNS" && entry.verdict !== "concerns") {
112818
+ return {
112819
+ blocked: true,
112820
+ reason: "PHASE_COUNCIL_INVALID",
112821
+ message: `Phase ${phase} cannot be completed: phase council evidence contains unrecognized verdict '${entry.verdict}'. Expected one of: APPROVE, CONCERNS, REJECT.`,
112822
+ agentsDispatched,
112823
+ agentsMissing: [],
112824
+ warnings: []
112825
+ };
112826
+ }
112827
+ }
112828
+ }
112829
+ } catch (readErr) {
112830
+ if (readErr.code !== "ENOENT") {
112831
+ safeWarn(`[phase_complete] Phase council evidence unreadable:`, readErr);
112832
+ }
112833
+ pcVerdictFound = false;
112834
+ }
112835
+ if (!pcVerdictFound) {
112836
+ return {
112837
+ blocked: true,
112838
+ reason: "PHASE_COUNCIL_REQUIRED",
112839
+ phase_council_required: true,
112840
+ message: `Phase ${phase} cannot be completed: council_mode is enabled and phase council evidence not found at .swarm/evidence/${phase}/phase-council.json. Convene a phase-level council (dispatch 5 members, collect verdicts, call submit_phase_council_verdicts) before completing the phase.`,
112841
+ agentsDispatched,
112842
+ agentsMissing: [],
112843
+ warnings: [
112844
+ `Phase council required — convene 5 council members (critic, reviewer, sme, test_engineer, explorer) for holistic phase review. Call submit_phase_council_verdicts to synthesize verdicts and write phase-council.json evidence.`
112845
+ ]
112846
+ };
112847
+ }
112848
+ if (pcQuorumSize === undefined || typeof pcQuorumSize !== "number") {
112849
+ return {
112850
+ blocked: true,
112851
+ reason: "PHASE_COUNCIL_MISSING_QUORUM",
112852
+ message: `Phase ${phase} cannot be completed: phase council evidence is missing quorumSize field.`,
112853
+ agentsDispatched,
112854
+ agentsMissing: [],
112855
+ warnings: []
112856
+ };
112857
+ }
112858
+ if (pcQuorumSize < 3) {
112859
+ return {
112860
+ blocked: true,
112861
+ reason: "PHASE_COUNCIL_INSUFFICIENT_QUORUM",
112862
+ message: `Phase ${phase} cannot be completed: phase council quorum (${pcQuorumSize}) is below minimum (3). Re-convene council with sufficient members.`,
112863
+ agentsDispatched,
112864
+ agentsMissing: [],
112865
+ warnings: []
112866
+ };
112867
+ }
112868
+ if (pcPhaseNumber === undefined || typeof pcPhaseNumber !== "number") {
112869
+ return {
112870
+ blocked: true,
112871
+ reason: "PHASE_COUNCIL_MISSING_PHASE",
112872
+ message: `Phase ${phase} cannot be completed: phase council evidence is missing phase_number field.`,
112873
+ agentsDispatched,
112874
+ agentsMissing: [],
112875
+ warnings: []
112876
+ };
112877
+ }
112878
+ if (pcPhaseNumber !== phase) {
112879
+ return {
112880
+ blocked: true,
112881
+ reason: "PHASE_COUNCIL_PHASE_MISMATCH",
112882
+ message: `Phase ${phase} cannot be completed: phase council evidence is for phase ${pcPhaseNumber}, not phase ${phase}. Run council for the correct phase.`,
112883
+ agentsDispatched,
112884
+ agentsMissing: [],
112885
+ warnings: []
112886
+ };
112887
+ }
112888
+ }
112889
+ }
112890
+ }
112891
+ } catch (pcError) {
112892
+ if (councilModeEnabled) {
112893
+ return {
112894
+ blocked: true,
112895
+ reason: "PHASE_COUNCIL_ERROR",
112896
+ message: `Phase ${phase} cannot be completed: phase council gate encountered an error when council_mode was enabled. Error: ${String(pcError)}`,
112897
+ agentsDispatched,
112898
+ agentsMissing: [],
112899
+ warnings: [`PHASE_COUNCIL_ERROR: ${String(pcError)}`]
112900
+ };
112901
+ } else {
112902
+ safeWarn(`[phase_complete] Phase council gate error (non-blocking):`, pcError);
112903
+ }
112904
+ }
112905
+ return { blocked: false, agentsDispatched, agentsMissing: [], warnings: [] };
112906
+ }
112907
+ // src/tools/phase-complete.ts
112140
112908
  init_resolve_working_directory();
112141
112909
  function safeWarn(message, error93) {
112142
112910
  try {
112143
112911
  warn(message, error93 instanceof Error ? error93.message : String(error93));
112144
112912
  } catch {}
112145
112913
  }
112914
+ var MAX_OUTPUT_BYTES5 = 512000;
112146
112915
  var TASK_GATE_INFERABLE_AGENTS = new Set([
112147
112916
  "coder",
112148
112917
  "reviewer",
@@ -112213,6 +112982,28 @@ function _getDelegationsSince(sessionID, sinceTimestamp) {
112213
112982
  function isValidRetroEntry(entry, phase) {
112214
112983
  return entry.type === "retrospective" && "phase_number" in entry && entry.phase_number === phase && "verdict" in entry && entry.verdict === "pass";
112215
112984
  }
112985
+ function blockedResult(phase, gateResult) {
112986
+ const {
112987
+ reason,
112988
+ message,
112989
+ agentsDispatched,
112990
+ agentsMissing,
112991
+ warnings,
112992
+ blocked: _blocked,
112993
+ ...extra
112994
+ } = gateResult;
112995
+ return JSON.stringify({
112996
+ success: false,
112997
+ phase,
112998
+ status: "blocked",
112999
+ reason,
113000
+ message,
113001
+ agentsDispatched,
113002
+ agentsMissing,
113003
+ warnings,
113004
+ ...extra
113005
+ }, null, 2);
113006
+ }
112216
113007
  async function executePhaseComplete(args2, workingDirectory, directory) {
112217
113008
  const phase = Number(args2.phase);
112218
113009
  const summary = args2.summary;
@@ -112351,719 +113142,66 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
112351
113142
  ]
112352
113143
  }, null, 2);
112353
113144
  }
113145
+ const gateCtx = {
113146
+ phase,
113147
+ dir,
113148
+ sessionID,
113149
+ pluginConfig: config3,
113150
+ agentsDispatched,
113151
+ safeWarn
113152
+ };
112354
113153
  if (hasActiveTurboMode(sessionID)) {
112355
113154
  warnings.push(`Turbo mode active — skipped completion-verify, drift-verifier, hallucination-guard, mutation-gate, phase-council, and final-council gates for phase ${phase}.`);
112356
113155
  } else {
112357
- try {
112358
- const completionResultRaw = await executeCompletionVerify({ phase }, dir);
112359
- const completionResult = JSON.parse(completionResultRaw);
112360
- if (completionResult.status === "blocked") {
112361
- return JSON.stringify({
112362
- success: false,
112363
- phase,
112364
- status: "blocked",
112365
- reason: "COMPLETION_INCOMPLETE",
112366
- message: `Phase ${phase} cannot be completed: ${completionResult.reason}`,
112367
- agentsDispatched,
112368
- agentsMissing: [],
112369
- warnings: completionResult.blockedTasks ? [
112370
- `Blocked tasks: ${completionResult.blockedTasks.map((t) => t.task_id).join(", ")}`
112371
- ] : []
112372
- }, null, 2);
113156
+ {
113157
+ const gateResult = await runCompletionVerifyGate(gateCtx);
113158
+ if (gateResult.blocked) {
113159
+ return blockedResult(phase, gateResult);
112373
113160
  }
112374
- } catch (completionError) {
112375
- safeWarn(`[phase_complete] Completion verify error (non-blocking):`, completionError);
113161
+ warnings.push(...gateResult.warnings);
112376
113162
  }
112377
- let driftCheckEnabled = true;
112378
- let driftHasSpecMd = false;
112379
- try {
112380
- const specMdPath = path127.join(dir, ".swarm", "spec.md");
112381
- driftHasSpecMd = fs90.existsSync(specMdPath);
112382
- const gatePlan = await loadPlan(dir);
112383
- if (gatePlan) {
112384
- const gatePlanId = derivePlanId(gatePlan);
112385
- const gateProfile = getProfile(dir, gatePlanId);
112386
- if (gateProfile) {
112387
- const gateSession = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
112388
- const gateOverrides = gateSession?.qaGateSessionOverrides ?? {};
112389
- const gateEffective = getEffectiveGates(gateProfile, gateOverrides);
112390
- driftCheckEnabled = gateEffective.drift_check === true;
112391
- }
112392
- }
112393
- } catch (gateLoadError) {
112394
- safeWarn(`[phase_complete] QA gate profile load error, drift_check defaults to enabled:`, gateLoadError);
112395
- }
112396
- if (!driftCheckEnabled) {
112397
- warnings.push(`drift_check gate is disabled. Drift verification was skipped for phase ${phase}.`);
112398
- } else {
112399
- let phaseType;
112400
- try {
112401
- const planPath = path127.join(dir, ".swarm", "plan.json");
112402
- if (fs90.existsSync(planPath)) {
112403
- const planRaw = fs90.readFileSync(planPath, "utf-8");
112404
- const plan = JSON.parse(planRaw);
112405
- const targetPhase = plan.phases?.find((p) => p.id === phase);
112406
- phaseType = targetPhase?.type;
112407
- }
112408
- } catch {}
112409
- if (phaseType === "non-code") {
112410
- warnings.push(`Phase ${phase} is annotated as 'non-code'. Drift verification was skipped per phase type annotation.`);
112411
- } else {
112412
- try {
112413
- const driftEvidencePath = path127.join(dir, ".swarm", "evidence", String(phase), "drift-verifier.json");
112414
- let driftVerdictFound = false;
112415
- let driftVerdictApproved = false;
112416
- try {
112417
- const driftEvidenceContent = fs90.readFileSync(driftEvidencePath, "utf-8");
112418
- const driftEvidence = JSON.parse(driftEvidenceContent);
112419
- const entries = driftEvidence.entries ?? [];
112420
- for (const entry of entries) {
112421
- if (typeof entry.type === "string" && entry.type.includes("drift") && typeof entry.verdict === "string") {
112422
- driftVerdictFound = true;
112423
- if (entry.verdict === "approved") {
112424
- driftVerdictApproved = true;
112425
- }
112426
- if (entry.verdict === "rejected" || typeof entry.summary === "string" && entry.summary.includes("NEEDS_REVISION")) {
112427
- return JSON.stringify({
112428
- success: false,
112429
- phase,
112430
- status: "blocked",
112431
- reason: "DRIFT_VERIFICATION_REJECTED",
112432
- message: `Phase ${phase} cannot be completed: drift verifier returned verdict '${entry.verdict}'. Address the drift issues before completing the phase.`,
112433
- agentsDispatched,
112434
- agentsMissing: [],
112435
- warnings: []
112436
- }, null, 2);
112437
- }
112438
- }
112439
- }
112440
- } catch (readError) {
112441
- if (readError.code !== "ENOENT") {
112442
- safeWarn(`[phase_complete] Drift verifier evidence unreadable:`, readError);
112443
- }
112444
- driftVerdictFound = false;
112445
- }
112446
- if (!driftVerdictFound) {
112447
- if (!driftHasSpecMd) {
112448
- let incompleteTaskCount = 0;
112449
- let planParseable = false;
112450
- try {
112451
- const planPath = path127.join(dir, ".swarm", "plan.json");
112452
- if (fs90.existsSync(planPath)) {
112453
- const planRaw = fs90.readFileSync(planPath, "utf-8");
112454
- const plan = JSON.parse(planRaw);
112455
- planParseable = true;
112456
- const planPhase = plan.phases?.find((p) => p.id === phase);
112457
- if (planPhase?.tasks) {
112458
- incompleteTaskCount = planPhase.tasks.filter((t) => t.status !== "completed" && t.status !== "closed").length;
112459
- }
112460
- }
112461
- } catch {}
112462
- if (!planParseable) {
112463
- warnings.push(`No spec.md found and drift verification evidence missing — consider running critic_drift_verifier before phase completion.`);
112464
- } else if (incompleteTaskCount > 0) {
112465
- warnings.push(`No spec.md found and drift verification evidence missing. Phase ${phase} has ${incompleteTaskCount} incomplete task(s) in plan.json — consider running critic_drift_verifier before phase completion.`);
112466
- } else {
112467
- warnings.push(`No spec.md found. Phase ${phase} tasks are all completed in plan.json. Drift verification was skipped.`);
112468
- }
112469
- } else {
112470
- return JSON.stringify({
112471
- success: false,
112472
- phase,
112473
- status: "blocked",
112474
- reason: "DRIFT_VERIFICATION_MISSING",
112475
- message: `Phase ${phase} cannot be completed: drift_check is enabled and drift verifier evidence not found at .swarm/evidence/${phase}/drift-verifier.json. Run drift verification before completing the phase.`,
112476
- agentsDispatched,
112477
- agentsMissing: [],
112478
- warnings: []
112479
- }, null, 2);
112480
- }
112481
- }
112482
- if (!driftVerdictApproved && driftVerdictFound) {
112483
- return JSON.stringify({
112484
- success: false,
112485
- phase,
112486
- status: "blocked",
112487
- reason: "DRIFT_VERIFICATION_REJECTED",
112488
- message: `Phase ${phase} cannot be completed: drift verifier verdict is not approved.`,
112489
- agentsDispatched,
112490
- agentsMissing: [],
112491
- warnings: []
112492
- }, null, 2);
112493
- }
112494
- } catch (driftError) {
112495
- return JSON.stringify({
112496
- success: false,
112497
- phase,
112498
- status: "blocked",
112499
- reason: "DRIFT_VERIFICATION_ERROR",
112500
- message: `Phase ${phase} cannot be completed: drift verification encountered an error: ${driftError instanceof Error ? driftError.message : String(driftError)}. This is a hard block — resolve the error before completing the phase.`,
112501
- agentsDispatched,
112502
- agentsMissing: [],
112503
- warnings: []
112504
- }, null, 2);
112505
- }
113163
+ {
113164
+ const gateResult = await runDriftGate(gateCtx);
113165
+ if (gateResult.blocked) {
113166
+ return blockedResult(phase, gateResult);
112506
113167
  }
113168
+ warnings.push(...gateResult.warnings);
112507
113169
  }
112508
- try {
112509
- const plan = await loadPlan(dir);
112510
- if (plan) {
112511
- const planId = derivePlanId(plan);
112512
- const profile = getProfile(dir, planId);
112513
- if (profile) {
112514
- const session2 = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
112515
- const overrides = session2?.qaGateSessionOverrides ?? {};
112516
- const effective = getEffectiveGates(profile, overrides);
112517
- if (effective.hallucination_guard === true) {
112518
- const hgPath = path127.join(dir, ".swarm", "evidence", String(phase), "hallucination-guard.json");
112519
- let hgVerdictFound = false;
112520
- let hgVerdictApproved = false;
112521
- try {
112522
- const hgContent = fs90.readFileSync(hgPath, "utf-8");
112523
- const hgBundle = JSON.parse(hgContent);
112524
- for (const entry of hgBundle.entries ?? []) {
112525
- if (typeof entry.type === "string" && entry.type.includes("hallucination") && typeof entry.verdict === "string") {
112526
- hgVerdictFound = true;
112527
- if (entry.verdict === "approved") {
112528
- hgVerdictApproved = true;
112529
- }
112530
- if (entry.verdict === "rejected" || typeof entry.summary === "string" && entry.summary.includes("NEEDS_REVISION")) {
112531
- return JSON.stringify({
112532
- success: false,
112533
- phase,
112534
- status: "blocked",
112535
- reason: "HALLUCINATION_VERIFICATION_REJECTED",
112536
- message: `Phase ${phase} cannot be completed: hallucination verifier returned verdict '${entry.verdict}'. Remove fabricated APIs/signatures and fix broken citations before completing the phase.`,
112537
- agentsDispatched,
112538
- agentsMissing: [],
112539
- warnings: []
112540
- }, null, 2);
112541
- }
112542
- }
112543
- }
112544
- } catch (readErr) {
112545
- if (readErr.code !== "ENOENT") {
112546
- safeWarn(`[phase_complete] Hallucination guard evidence unreadable:`, readErr);
112547
- }
112548
- hgVerdictFound = false;
112549
- }
112550
- if (!hgVerdictFound) {
112551
- return JSON.stringify({
112552
- success: false,
112553
- phase,
112554
- status: "blocked",
112555
- reason: "HALLUCINATION_VERIFICATION_MISSING",
112556
- message: `Phase ${phase} cannot be completed: hallucination_guard is enabled and evidence not found at .swarm/evidence/${phase}/hallucination-guard.json. Delegate to critic_hallucination_verifier and call write_hallucination_evidence before completing the phase.`,
112557
- agentsDispatched,
112558
- agentsMissing: [],
112559
- warnings: []
112560
- }, null, 2);
112561
- }
112562
- if (!hgVerdictApproved) {
112563
- return JSON.stringify({
112564
- success: false,
112565
- phase,
112566
- status: "blocked",
112567
- reason: "HALLUCINATION_VERIFICATION_REJECTED",
112568
- message: `Phase ${phase} cannot be completed: hallucination verifier verdict is not approved.`,
112569
- agentsDispatched,
112570
- agentsMissing: [],
112571
- warnings: []
112572
- }, null, 2);
112573
- }
112574
- }
112575
- }
113170
+ {
113171
+ const gateResult = await runHallucinationGate(gateCtx);
113172
+ if (gateResult.blocked) {
113173
+ return blockedResult(phase, gateResult);
112576
113174
  }
112577
- } catch (hgError) {
112578
- safeWarn(`[phase_complete] Hallucination guard error (non-blocking):`, hgError);
113175
+ warnings.push(...gateResult.warnings);
112579
113176
  }
112580
- try {
112581
- const plan = await loadPlan(dir);
112582
- if (plan) {
112583
- const planId = derivePlanId(plan);
112584
- const profile = getProfile(dir, planId);
112585
- if (profile) {
112586
- const session2 = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
112587
- const overrides = session2?.qaGateSessionOverrides ?? {};
112588
- const effective = getEffectiveGates(profile, overrides);
112589
- if (effective.mutation_test === true) {
112590
- const mgPath = path127.join(dir, ".swarm", "evidence", String(phase), "mutation-gate.json");
112591
- let mgVerdictFound = false;
112592
- let mgVerdict;
112593
- try {
112594
- const mgContent = fs90.readFileSync(mgPath, "utf-8");
112595
- const mgBundle = JSON.parse(mgContent);
112596
- for (const entry of mgBundle.entries ?? []) {
112597
- if (typeof entry.type === "string" && entry.type === "mutation-gate" && typeof entry.verdict === "string") {
112598
- mgVerdictFound = true;
112599
- mgVerdict = entry.verdict;
112600
- if (entry.verdict === "fail") {
112601
- return JSON.stringify({
112602
- success: false,
112603
- phase,
112604
- status: "blocked",
112605
- reason: "MUTATION_GATE_FAIL",
112606
- message: `Phase ${phase} cannot be completed: mutation gate returned verdict 'fail'. Resolve surviving mutants or lower the kill-rate threshold before completing the phase.`,
112607
- agentsDispatched,
112608
- agentsMissing: [],
112609
- warnings: []
112610
- }, null, 2);
112611
- } else if (!["pass", "warn", "skip"].includes(entry.verdict)) {
112612
- return JSON.stringify({
112613
- success: false,
112614
- phase,
112615
- status: "blocked",
112616
- reason: "MUTATION_GATE_FAIL",
112617
- message: `Phase ${phase} cannot be completed: mutation gate evidence contains unrecognized verdict '${entry.verdict}'. Expected one of: pass, warn, fail, skip.`,
112618
- agentsDispatched,
112619
- agentsMissing: [],
112620
- warnings: []
112621
- }, null, 2);
112622
- }
112623
- }
112624
- }
112625
- } catch (readErr) {
112626
- if (readErr.code !== "ENOENT") {
112627
- safeWarn(`[phase_complete] Mutation gate evidence unreadable:`, readErr);
112628
- }
112629
- mgVerdictFound = false;
112630
- }
112631
- if (!mgVerdictFound) {
112632
- return JSON.stringify({
112633
- success: false,
112634
- phase,
112635
- status: "blocked",
112636
- reason: "MUTATION_GATE_MISSING",
112637
- message: `Phase ${phase} cannot be completed: mutation_test is enabled and evidence not found at .swarm/evidence/${phase}/mutation-gate.json. Run mutation_test, then call write_mutation_evidence before completing the phase.`,
112638
- agentsDispatched,
112639
- agentsMissing: [],
112640
- warnings: []
112641
- }, null, 2);
112642
- }
112643
- if (mgVerdict === "warn") {
112644
- safeWarn(`[phase_complete] Mutation gate verdict is 'warn' for phase ${phase} — proceeding with warning`, undefined);
112645
- }
112646
- }
112647
- }
113177
+ {
113178
+ const gateResult = await runMutationGate(gateCtx);
113179
+ if (gateResult.blocked) {
113180
+ return blockedResult(phase, gateResult);
112648
113181
  }
112649
- } catch (mgError) {
112650
- safeWarn(`[phase_complete] Mutation gate error (non-blocking):`, mgError);
113182
+ warnings.push(...gateResult.warnings);
112651
113183
  }
112652
- let councilModeEnabled = false;
112653
- try {
112654
- const plan = await loadPlan(dir);
112655
- if (plan) {
112656
- const planId = derivePlanId(plan);
112657
- const profile = getProfile(dir, planId);
112658
- if (profile) {
112659
- const session2 = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
112660
- const overrides = session2?.qaGateSessionOverrides ?? {};
112661
- const effective = getEffectiveGates(profile, overrides);
112662
- if (effective.council_mode === true) {
112663
- councilModeEnabled = true;
112664
- const pcPath = path127.join(dir, ".swarm", "evidence", String(phase), "phase-council.json");
112665
- let pcVerdictFound = false;
112666
- let _pcVerdict;
112667
- let pcQuorumSize;
112668
- let pcTimestamp;
112669
- let pcPhaseNumber;
112670
- try {
112671
- const pcContent = fs90.readFileSync(pcPath, "utf-8");
112672
- const pcBundle = JSON.parse(pcContent);
112673
- for (const entry of pcBundle.entries ?? []) {
112674
- if (typeof entry.type === "string" && entry.type === "phase-council" && typeof entry.verdict === "string") {
112675
- pcVerdictFound = true;
112676
- _pcVerdict = entry.verdict;
112677
- pcQuorumSize = typeof entry.quorumSize === "number" ? entry.quorumSize : undefined;
112678
- pcTimestamp = typeof entry.timestamp === "string" ? entry.timestamp : undefined;
112679
- pcPhaseNumber = typeof entry.phase_number === "number" ? entry.phase_number : typeof entry.phase === "number" ? entry.phase : undefined;
112680
- const now2 = new Date;
112681
- const pcTime = pcTimestamp ? new Date(pcTimestamp) : null;
112682
- if (!pcTime || Number.isNaN(pcTime.getTime())) {
112683
- return JSON.stringify({
112684
- success: false,
112685
- phase,
112686
- status: "blocked",
112687
- reason: "PHASE_COUNCIL_INVALID_TIMESTAMP",
112688
- message: `Phase ${phase} cannot be completed: phase council evidence has missing or invalid timestamp.`,
112689
- agentsDispatched,
112690
- agentsMissing: [],
112691
- warnings: []
112692
- }, null, 2);
112693
- }
112694
- const maxAge = 24 * 60 * 60 * 1000;
112695
- if (pcTime.getTime() > now2.getTime()) {
112696
- return JSON.stringify({
112697
- success: false,
112698
- phase,
112699
- status: "blocked",
112700
- reason: "PHASE_COUNCIL_FUTURE_TIMESTAMP",
112701
- message: `Phase ${phase} cannot be completed: phase council evidence timestamp is in the future.`,
112702
- agentsDispatched,
112703
- agentsMissing: [],
112704
- warnings: []
112705
- }, null, 2);
112706
- }
112707
- if (now2.getTime() - pcTime.getTime() > maxAge) {
112708
- return JSON.stringify({
112709
- success: false,
112710
- phase,
112711
- status: "blocked",
112712
- reason: "PHASE_COUNCIL_STALE_EVIDENCE",
112713
- message: `Phase ${phase} cannot be completed: phase council evidence is older than 24 hours. Re-convene council for fresh review.`,
112714
- agentsDispatched,
112715
- agentsMissing: [],
112716
- warnings: []
112717
- }, null, 2);
112718
- }
112719
- if (entry.verdict === "REJECT" || entry.verdict === "reject") {
112720
- const requiredFixes = entry.requiredFixes ?? entry.required_fixes ?? [];
112721
- const fixesDetail = Array.isArray(requiredFixes) && requiredFixes.length > 0 ? `
112722
- Required fixes: ${requiredFixes.map((f) => f.detail ?? JSON.stringify(f)).join("; ")}` : "";
112723
- return JSON.stringify({
112724
- success: false,
112725
- phase,
112726
- status: "blocked",
112727
- reason: "PHASE_COUNCIL_REJECTED",
112728
- message: `Phase ${phase} cannot be completed: phase council returned verdict 'REJECT'. Address the required fixes before completing the phase.${fixesDetail}`,
112729
- agentsDispatched,
112730
- agentsMissing: [],
112731
- warnings: []
112732
- }, null, 2);
112733
- }
112734
- if (entry.verdict === "CONCERNS" || entry.verdict === "concerns") {
112735
- const phaseConcernsAllow = config3.council?.phaseConcernsAllowComplete ?? true;
112736
- if (!phaseConcernsAllow) {
112737
- const advisoryNotes = entry.advisoryNotes ?? entry.advisory_notes ?? [];
112738
- const notesDetail = Array.isArray(advisoryNotes) && advisoryNotes.length > 0 ? `
112739
- Advisory notes: ${advisoryNotes.join("; ")}` : "";
112740
- return JSON.stringify({
112741
- success: false,
112742
- phase,
112743
- status: "blocked",
112744
- reason: "PHASE_COUNCIL_CONCERNS",
112745
- message: `Phase ${phase} cannot be completed: phase council returned verdict 'CONCERNS'.${notesDetail}`,
112746
- agentsDispatched,
112747
- agentsMissing: [],
112748
- warnings: []
112749
- }, null, 2);
112750
- }
112751
- safeWarn(`[phase_complete] Phase council returned CONCERNS for phase ${phase} — proceeding (phaseConcernsAllowComplete is enabled)`, undefined);
112752
- }
112753
- if (entry.verdict !== "APPROVE" && entry.verdict !== "approve" && entry.verdict !== "CONCERNS" && entry.verdict !== "concerns") {
112754
- return JSON.stringify({
112755
- success: false,
112756
- phase,
112757
- status: "blocked",
112758
- reason: "PHASE_COUNCIL_INVALID",
112759
- message: `Phase ${phase} cannot be completed: phase council evidence contains unrecognized verdict '${entry.verdict}'. Expected one of: APPROVE, CONCERNS, REJECT.`,
112760
- agentsDispatched,
112761
- agentsMissing: [],
112762
- warnings: []
112763
- }, null, 2);
112764
- }
112765
- }
112766
- }
112767
- } catch (readErr) {
112768
- if (readErr.code !== "ENOENT") {
112769
- safeWarn(`[phase_complete] Phase council evidence unreadable:`, readErr);
112770
- }
112771
- pcVerdictFound = false;
112772
- }
112773
- if (!pcVerdictFound) {
112774
- return JSON.stringify({
112775
- success: false,
112776
- phase,
112777
- status: "blocked",
112778
- reason: "PHASE_COUNCIL_REQUIRED",
112779
- phase_council_required: true,
112780
- message: `Phase ${phase} cannot be completed: council_mode is enabled and phase council evidence not found at .swarm/evidence/${phase}/phase-council.json. Convene a phase-level council (dispatch 5 members, collect verdicts, call submit_phase_council_verdicts) before completing the phase.`,
112781
- agentsDispatched,
112782
- agentsMissing: [],
112783
- warnings: [
112784
- `Phase council required — convene 5 council members (critic, reviewer, sme, test_engineer, explorer) for holistic phase review. Call submit_phase_council_verdicts to synthesize verdicts and write phase-council.json evidence.`
112785
- ]
112786
- }, null, 2);
112787
- }
112788
- if (pcQuorumSize === undefined || typeof pcQuorumSize !== "number") {
112789
- return JSON.stringify({
112790
- success: false,
112791
- phase,
112792
- status: "blocked",
112793
- reason: "PHASE_COUNCIL_MISSING_QUORUM",
112794
- message: `Phase ${phase} cannot be completed: phase council evidence is missing quorumSize field.`,
112795
- agentsDispatched,
112796
- agentsMissing: [],
112797
- warnings: []
112798
- }, null, 2);
112799
- }
112800
- if (pcQuorumSize < 3) {
112801
- return JSON.stringify({
112802
- success: false,
112803
- phase,
112804
- status: "blocked",
112805
- reason: "PHASE_COUNCIL_INSUFFICIENT_QUORUM",
112806
- message: `Phase ${phase} cannot be completed: phase council quorum (${pcQuorumSize}) is below minimum (3). Re-convene council with sufficient members.`,
112807
- agentsDispatched,
112808
- agentsMissing: [],
112809
- warnings: []
112810
- }, null, 2);
112811
- }
112812
- if (pcPhaseNumber === undefined || typeof pcPhaseNumber !== "number") {
112813
- return JSON.stringify({
112814
- success: false,
112815
- phase,
112816
- status: "blocked",
112817
- reason: "PHASE_COUNCIL_MISSING_PHASE",
112818
- message: `Phase ${phase} cannot be completed: phase council evidence is missing phase_number field.`,
112819
- agentsDispatched,
112820
- agentsMissing: [],
112821
- warnings: []
112822
- }, null, 2);
112823
- }
112824
- if (pcPhaseNumber !== phase) {
112825
- return JSON.stringify({
112826
- success: false,
112827
- phase,
112828
- status: "blocked",
112829
- reason: "PHASE_COUNCIL_PHASE_MISMATCH",
112830
- message: `Phase ${phase} cannot be completed: phase council evidence is for phase ${pcPhaseNumber}, not phase ${phase}. Run council for the correct phase.`,
112831
- agentsDispatched,
112832
- agentsMissing: [],
112833
- warnings: []
112834
- }, null, 2);
112835
- }
112836
- }
112837
- }
112838
- }
112839
- } catch (pcError) {
112840
- if (councilModeEnabled) {
112841
- warnings.push(`PHASE_COUNCIL_ERROR: ${String(pcError)}`);
112842
- return JSON.stringify({
112843
- success: false,
112844
- phase,
112845
- status: "blocked",
112846
- reason: "PHASE_COUNCIL_ERROR",
112847
- message: `Phase ${phase} cannot be completed: phase council gate encountered an error when council_mode was enabled. Error: ${String(pcError)}`,
112848
- agentsDispatched,
112849
- agentsMissing: [],
112850
- warnings: [`PHASE_COUNCIL_ERROR: ${String(pcError)}`]
112851
- }, null, 2);
112852
- } else {
112853
- safeWarn(`[phase_complete] Phase council gate error (non-blocking):`, pcError);
113184
+ {
113185
+ const gateResult = await runPhaseCouncilGate(gateCtx);
113186
+ if (gateResult.blocked) {
113187
+ return blockedResult(phase, gateResult);
112854
113188
  }
113189
+ warnings.push(...gateResult.warnings);
112855
113190
  }
112856
113191
  }
112857
113192
  if (config3.architectural_supervision?.enabled && config3.architectural_supervision.mode === "gate") {
112858
- const asConfig = config3.architectural_supervision;
112859
- const summarizeFindings = (findings) => {
112860
- if (!Array.isArray(findings) || findings.length === 0)
112861
- return "";
112862
- const details = findings.map((f) => f && typeof f === "object" && typeof f.description === "string" ? f.description : undefined).filter((d) => Boolean(d));
112863
- return details.length > 0 ? `
112864
- Findings: ${details.join("; ")}` : "";
112865
- };
112866
- const asBlocked = (reason, message2) => JSON.stringify({
112867
- success: false,
112868
- phase,
112869
- status: "blocked",
112870
- reason,
112871
- message: message2,
112872
- agentsDispatched,
112873
- agentsMissing: [],
112874
- warnings: []
112875
- }, null, 2);
112876
- let asEntry = null;
112877
- try {
112878
- asEntry = readSupervisorReportRaw(dir, phase);
112879
- } catch (asError) {
112880
- return asBlocked("ARCH_SUPERVISOR_ERROR", `Phase ${phase} cannot be completed: architecture supervisor gate encountered an error. Error: ${String(asError)}`);
112881
- }
112882
- if (!asEntry) {
112883
- return asBlocked("ARCH_SUPERVISOR_REQUIRED", `Phase ${phase} cannot be completed: architectural_supervision gate mode is enabled and no architecture supervisor evidence was found at .swarm/evidence/${phase}/architecture-supervisor.json. Dispatch critic_architecture_supervisor with the phase + agent summaries, then call write_architecture_supervisor_evidence.`);
112884
- }
112885
- const now2 = new Date;
112886
- const asTime = asEntry.timestamp ? new Date(asEntry.timestamp) : null;
112887
- if (!asTime || Number.isNaN(asTime.getTime())) {
112888
- return asBlocked("ARCH_SUPERVISOR_INVALID_TIMESTAMP", `Phase ${phase} cannot be completed: architecture supervisor evidence has a missing or invalid timestamp.`);
112889
- }
112890
- if (asTime.getTime() > now2.getTime()) {
112891
- return asBlocked("ARCH_SUPERVISOR_FUTURE_TIMESTAMP", `Phase ${phase} cannot be completed: architecture supervisor evidence timestamp is in the future.`);
112892
- }
112893
- if (now2.getTime() - asTime.getTime() > 24 * 60 * 60 * 1000) {
112894
- return asBlocked("ARCH_SUPERVISOR_STALE_EVIDENCE", `Phase ${phase} cannot be completed: architecture supervisor evidence is older than 24 hours. Re-run the supervisor for fresh review.`);
112895
- }
112896
- if (typeof asEntry.phase_number !== "number" || asEntry.phase_number !== phase) {
112897
- return asBlocked("ARCH_SUPERVISOR_PHASE_MISMATCH", `Phase ${phase} cannot be completed: architecture supervisor evidence is for phase ${String(asEntry.phase_number)}, not phase ${phase}.`);
112898
- }
112899
- const asVerdict = asEntry.verdict;
112900
- if (asVerdict === "REJECT") {
112901
- return asBlocked("ARCH_SUPERVISOR_REJECTED", `Phase ${phase} cannot be completed: architecture supervisor returned verdict 'REJECT'. Address the system-level findings before completing the phase.${summarizeFindings(asEntry.findings)}`);
112902
- }
112903
- if (asVerdict === "CONCERNS") {
112904
- if (asConfig.allow_concerns_to_complete === false) {
112905
- return asBlocked("ARCH_SUPERVISOR_CONCERNS", `Phase ${phase} cannot be completed: architecture supervisor returned verdict 'CONCERNS' and allow_concerns_to_complete is disabled.${summarizeFindings(asEntry.findings)}`);
112906
- }
112907
- safeWarn(`[phase_complete] Architecture supervisor returned CONCERNS for phase ${phase} — proceeding (allow_concerns_to_complete is enabled)`, undefined);
112908
- } else if (asVerdict !== "APPROVE") {
112909
- return asBlocked("ARCH_SUPERVISOR_INVALID", `Phase ${phase} cannot be completed: architecture supervisor evidence contains unrecognized verdict '${String(asVerdict)}'. Expected one of: APPROVE, CONCERNS, REJECT.`);
113193
+ const gateResult = await runArchitectureSupervisorGate(gateCtx);
113194
+ if (gateResult.blocked) {
113195
+ return blockedResult(phase, gateResult);
112910
113196
  }
113197
+ warnings.push(...gateResult.warnings);
112911
113198
  }
112912
113199
  if (!hasActiveTurboMode(sessionID)) {
112913
- let finalCouncilEnabled = false;
112914
- try {
112915
- const plan = await loadPlan(dir);
112916
- if (plan) {
112917
- const lastPhaseId = plan.phases[plan.phases.length - 1]?.id;
112918
- if (lastPhaseId !== undefined && phase === lastPhaseId) {
112919
- const planId = derivePlanId(plan);
112920
- const profile = getProfile(dir, planId);
112921
- if (profile) {
112922
- const session2 = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
112923
- const overrides = session2?.qaGateSessionOverrides ?? {};
112924
- const effective = getEffectiveGates(profile, overrides);
112925
- if (effective.final_council === true) {
112926
- finalCouncilEnabled = true;
112927
- const fcPath = path127.join(dir, ".swarm", "evidence", "final-council.json");
112928
- let fcVerdictFound = false;
112929
- let _fcVerdict;
112930
- try {
112931
- const fcContent = fs90.readFileSync(fcPath, "utf-8");
112932
- const fcBundle = JSON.parse(fcContent);
112933
- for (const entry of fcBundle.entries ?? []) {
112934
- if (typeof entry.type === "string" && entry.type === "final-council" && typeof entry.verdict === "string") {
112935
- fcVerdictFound = true;
112936
- _fcVerdict = entry.verdict;
112937
- if (plan) {
112938
- const currentPlanId = derivePlanId(plan);
112939
- if (entry.plan_id && entry.plan_id !== currentPlanId) {
112940
- return JSON.stringify({
112941
- success: false,
112942
- phase,
112943
- status: "blocked",
112944
- reason: "final_council_plan_mismatch",
112945
- message: `Final council evidence belongs to a different plan (evidence: ${entry.plan_id}, current: ${currentPlanId}). Re-run the final council.`,
112946
- agentsDispatched,
112947
- agentsMissing: [],
112948
- warnings: []
112949
- }, null, 2);
112950
- }
112951
- if (!entry.plan_id) {
112952
- return JSON.stringify({
112953
- success: false,
112954
- phase,
112955
- status: "blocked",
112956
- reason: "FINAL_COUNCIL_PLAN_ID_REQUIRED",
112957
- message: `Phase ${phase} (last phase) cannot be completed: final council evidence is missing plan_id binding. Re-run the final council to generate evidence with plan identity.`,
112958
- agentsDispatched,
112959
- agentsMissing: [],
112960
- warnings: []
112961
- }, null, 2);
112962
- }
112963
- }
112964
- if (typeof entry.quorumSize !== "number" || !Number.isFinite(entry.quorumSize) || entry.quorumSize < 5) {
112965
- return JSON.stringify({
112966
- success: false,
112967
- phase,
112968
- status: "blocked",
112969
- reason: "FINAL_COUNCIL_MISSING_QUORUM",
112970
- 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.`,
112971
- agentsDispatched,
112972
- agentsMissing: [],
112973
- warnings: []
112974
- }, null, 2);
112975
- }
112976
- const requiredFinalCouncilMembers = [
112977
- "critic",
112978
- "reviewer",
112979
- "sme",
112980
- "test_engineer",
112981
- "explorer"
112982
- ];
112983
- const membersVoted = Array.isArray(entry.membersVoted) ? entry.membersVoted.filter((member) => typeof member === "string") : [];
112984
- const membersAbsent = Array.isArray(entry.membersAbsent) ? entry.membersAbsent.filter((member) => typeof member === "string") : [];
112985
- const distinctMembersVoted = new Set(membersVoted);
112986
- const hasAllRequiredMembers = requiredFinalCouncilMembers.every((member) => distinctMembersVoted.has(member)) && distinctMembersVoted.size === requiredFinalCouncilMembers.length && membersAbsent.length === 0;
112987
- if (!hasAllRequiredMembers) {
112988
- return JSON.stringify({
112989
- success: false,
112990
- phase,
112991
- status: "blocked",
112992
- reason: "FINAL_COUNCIL_MISSING_QUORUM",
112993
- 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.`,
112994
- agentsDispatched,
112995
- agentsMissing: [],
112996
- warnings: []
112997
- }, null, 2);
112998
- }
112999
- if (entry.verdict === "rejected" || entry.verdict === "REJECTED") {
113000
- return JSON.stringify({
113001
- success: false,
113002
- phase,
113003
- status: "blocked",
113004
- reason: "FINAL_COUNCIL_REJECTED",
113005
- message: `Phase ${phase} (last phase) cannot be completed: final council returned verdict 'REJECTED'. Address the required fixes before completing the project.`,
113006
- agentsDispatched,
113007
- agentsMissing: [],
113008
- warnings: []
113009
- }, null, 2);
113010
- }
113011
- if (entry.verdict !== "approved" && entry.verdict !== "APPROVED") {
113012
- return JSON.stringify({
113013
- success: false,
113014
- phase,
113015
- status: "blocked",
113016
- reason: "FINAL_COUNCIL_INVALID_VERDICT",
113017
- message: `Phase ${phase} (last phase) cannot be completed: final council evidence contains unrecognized verdict '${entry.verdict}'. Expected 'approved'.`,
113018
- agentsDispatched,
113019
- agentsMissing: [],
113020
- warnings: []
113021
- }, null, 2);
113022
- }
113023
- }
113024
- }
113025
- } catch (readErr) {
113026
- if (readErr.code !== "ENOENT") {
113027
- safeWarn(`[phase_complete] Final council evidence unreadable:`, readErr);
113028
- }
113029
- fcVerdictFound = false;
113030
- }
113031
- if (!fcVerdictFound) {
113032
- return JSON.stringify({
113033
- success: false,
113034
- phase,
113035
- status: "blocked",
113036
- reason: "FINAL_COUNCIL_REQUIRED",
113037
- final_council_required: true,
113038
- 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.`,
113039
- agentsDispatched,
113040
- agentsMissing: [],
113041
- warnings: [
113042
- `Final council required - dispatch the five project-scoped council members, then call write_final_council_evidence to persist quorumed evidence.`
113043
- ]
113044
- }, null, 2);
113045
- }
113046
- }
113047
- }
113048
- }
113049
- }
113050
- } catch (fcError) {
113051
- if (finalCouncilEnabled) {
113052
- warnings.push(`FINAL_COUNCIL_ERROR: ${String(fcError)}`);
113053
- return JSON.stringify({
113054
- success: false,
113055
- phase,
113056
- status: "blocked",
113057
- reason: "FINAL_COUNCIL_ERROR",
113058
- message: `Phase ${phase} (last phase) cannot be completed: final council gate encountered an error. Error: ${String(fcError)}`,
113059
- agentsDispatched,
113060
- agentsMissing: [],
113061
- warnings: [`FINAL_COUNCIL_ERROR: ${String(fcError)}`]
113062
- }, null, 2);
113063
- } else {
113064
- safeWarn(`[phase_complete] Final council gate error (non-blocking):`, fcError);
113065
- }
113200
+ const gateResult = await runFinalCouncilGate(gateCtx);
113201
+ if (gateResult.blocked) {
113202
+ return blockedResult(phase, gateResult);
113066
113203
  }
113204
+ warnings.push(...gateResult.warnings);
113067
113205
  }
113068
113206
  {
113069
113207
  const approval = verifyFullAutoPhaseApproval(dir, sessionID, phase, config3);
@@ -113112,7 +113250,7 @@ Findings: ${details.join("; ")}` : "";
113112
113250
  }
113113
113251
  if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
113114
113252
  try {
113115
- const projectName = path127.basename(dir);
113253
+ const projectName = path132.basename(dir);
113116
113254
  const curationResult = await curateAndStoreSwarm(retroEntry.lessons_learned, projectName, { phase_number: phase }, dir, knowledgeConfig);
113117
113255
  if (curationResult) {
113118
113256
  const sessionState = swarmState.agentSessions.get(sessionID);
@@ -113212,14 +113350,14 @@ Findings: ${details.join("; ")}` : "";
113212
113350
  const markerPath = validateSwarmPath(dir, "skill-usage-last-processed.json");
113213
113351
  let sinceTimestamp;
113214
113352
  try {
113215
- const markerData = JSON.parse(fs90.readFileSync(markerPath, "utf-8"));
113353
+ const markerData = JSON.parse(fs95.readFileSync(markerPath, "utf-8"));
113216
113354
  sinceTimestamp = markerData.lastProcessedTimestamp;
113217
113355
  } catch {}
113218
113356
  const feedbackResult = await applySkillUsageFeedback(dir, {
113219
113357
  sinceTimestamp
113220
113358
  });
113221
113359
  try {
113222
- fs90.writeFileSync(markerPath, JSON.stringify({ lastProcessedTimestamp: new Date().toISOString() }), "utf-8");
113360
+ fs95.writeFileSync(markerPath, JSON.stringify({ lastProcessedTimestamp: new Date().toISOString() }), "utf-8");
113223
113361
  } catch {}
113224
113362
  if (feedbackResult.processed > 0) {
113225
113363
  const sessionState = swarmState.agentSessions.get(sessionID);
@@ -113239,7 +113377,7 @@ Findings: ${details.join("; ")}` : "";
113239
113377
  let phaseRequiredAgents;
113240
113378
  try {
113241
113379
  const planPath = validateSwarmPath(dir, "plan.json");
113242
- const planRaw = fs90.readFileSync(planPath, "utf-8");
113380
+ const planRaw = fs95.readFileSync(planPath, "utf-8");
113243
113381
  const plan = JSON.parse(planRaw);
113244
113382
  const phaseObj = plan.phases.find((p) => p.id === phase);
113245
113383
  phaseRequiredAgents = phaseObj?.required_agents;
@@ -113254,7 +113392,7 @@ Findings: ${details.join("; ")}` : "";
113254
113392
  if (agentsMissing.length > 0) {
113255
113393
  try {
113256
113394
  const planPath = validateSwarmPath(dir, "plan.json");
113257
- const planRaw = fs90.readFileSync(planPath, "utf-8");
113395
+ const planRaw = fs95.readFileSync(planPath, "utf-8");
113258
113396
  const plan = JSON.parse(planRaw);
113259
113397
  const targetPhase = plan.phases.find((p) => p.id === phase);
113260
113398
  if (targetPhase && targetPhase.tasks.length > 0 && canInferMissingAgentsFromTaskGates(agentsMissing) && await allCompletedTasksHavePassedGateEvidence(dir, targetPhase.tasks)) {
@@ -113294,7 +113432,7 @@ Findings: ${details.join("; ")}` : "";
113294
113432
  if (phaseCompleteConfig.regression_sweep?.enforce) {
113295
113433
  try {
113296
113434
  const planPath = validateSwarmPath(dir, "plan.json");
113297
- const planRaw = fs90.readFileSync(planPath, "utf-8");
113435
+ const planRaw = fs95.readFileSync(planPath, "utf-8");
113298
113436
  const plan = JSON.parse(planRaw);
113299
113437
  const targetPhase = plan.phases.find((p) => p.id === phase);
113300
113438
  if (targetPhase) {
@@ -113348,7 +113486,7 @@ Findings: ${details.join("; ")}` : "";
113348
113486
  }
113349
113487
  try {
113350
113488
  const eventsPath = validateSwarmPath(dir, "events.jsonl");
113351
- fs90.appendFileSync(eventsPath, `${JSON.stringify(event)}
113489
+ fs95.appendFileSync(eventsPath, `${JSON.stringify(event)}
113352
113490
  `, "utf-8");
113353
113491
  } catch (writeError) {
113354
113492
  warnings.push(`Warning: failed to write phase complete event: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
@@ -113423,12 +113561,12 @@ Findings: ${details.join("; ")}` : "";
113423
113561
  warnings.push(`Warning: failed to update plan.json phase status`);
113424
113562
  try {
113425
113563
  const planPath = validateSwarmPath(dir, "plan.json");
113426
- const planRaw = fs90.readFileSync(planPath, "utf-8");
113564
+ const planRaw = fs95.readFileSync(planPath, "utf-8");
113427
113565
  const plan2 = JSON.parse(planRaw);
113428
113566
  const phaseObj = plan2.phases.find((p) => p.id === phase);
113429
113567
  if (phaseObj) {
113430
113568
  phaseObj.status = "complete";
113431
- fs90.writeFileSync(planPath, JSON.stringify(plan2, null, 2), "utf-8");
113569
+ fs95.writeFileSync(planPath, JSON.stringify(plan2, null, 2), "utf-8");
113432
113570
  }
113433
113571
  } catch {}
113434
113572
  } else if (plan) {
@@ -113465,12 +113603,12 @@ Findings: ${details.join("; ")}` : "";
113465
113603
  warnings.push(`Warning: failed to update plan.json phase status`);
113466
113604
  try {
113467
113605
  const planPath = validateSwarmPath(dir, "plan.json");
113468
- const planRaw = fs90.readFileSync(planPath, "utf-8");
113606
+ const planRaw = fs95.readFileSync(planPath, "utf-8");
113469
113607
  const plan = JSON.parse(planRaw);
113470
113608
  const phaseObj = plan.phases.find((p) => p.id === phase);
113471
113609
  if (phaseObj) {
113472
113610
  phaseObj.status = "complete";
113473
- fs90.writeFileSync(planPath, JSON.stringify(plan, null, 2), "utf-8");
113611
+ fs95.writeFileSync(planPath, JSON.stringify(plan, null, 2), "utf-8");
113474
113612
  }
113475
113613
  } catch {}
113476
113614
  }
@@ -113480,7 +113618,32 @@ Findings: ${details.join("; ")}` : "";
113480
113618
  }
113481
113619
  await flushPendingSnapshot(dir);
113482
113620
  await writeCheckpoint(dir).catch(() => {});
113483
- return JSON.stringify({ ...result, timestamp: event.timestamp, duration_ms: durationMs }, null, 2);
113621
+ const outputData = {
113622
+ ...result,
113623
+ timestamp: event.timestamp,
113624
+ duration_ms: durationMs
113625
+ };
113626
+ return _buildOutputJson(outputData);
113627
+ }
113628
+ function _buildOutputJson(outputData) {
113629
+ let json3 = JSON.stringify(outputData, null, 2);
113630
+ if (json3.length > MAX_OUTPUT_BYTES5) {
113631
+ const truncated = {
113632
+ _truncated: true,
113633
+ _truncation_reason: `Output exceeded MAX_OUTPUT_BYTES (${MAX_OUTPUT_BYTES5}) limit`,
113634
+ phase: outputData.phase,
113635
+ success: outputData.success,
113636
+ status: outputData.status,
113637
+ message: outputData.message,
113638
+ agentsDispatched: outputData.agentsDispatched?.slice(0, 10),
113639
+ agentsMissing: outputData.agentsMissing?.slice(0, 10),
113640
+ warnings: ["(output truncated — full output exceeded size limit)"],
113641
+ timestamp: outputData.timestamp,
113642
+ duration_ms: outputData.duration_ms
113643
+ };
113644
+ json3 = JSON.stringify(truncated, null, 2);
113645
+ }
113646
+ return json3;
113484
113647
  }
113485
113648
  var phase_complete = createSwarmTool({
113486
113649
  description: "Mark a phase as complete and track which agents were dispatched. Used for phase completion gating and tracking. Accepts phase number and optional summary. Returns list of agents that were dispatched.",
@@ -113528,9 +113691,9 @@ init_discovery();
113528
113691
  init_utils();
113529
113692
  init_bun_compat();
113530
113693
  init_create_tool();
113531
- import * as fs91 from "node:fs";
113532
- import * as path128 from "node:path";
113533
- var MAX_OUTPUT_BYTES5 = 52428800;
113694
+ import * as fs96 from "node:fs";
113695
+ import * as path133 from "node:path";
113696
+ var MAX_OUTPUT_BYTES6 = 52428800;
113534
113697
  var AUDIT_TIMEOUT_MS = 120000;
113535
113698
  function isValidEcosystem(value) {
113536
113699
  return typeof value === "string" && [
@@ -113557,31 +113720,31 @@ function validateArgs3(args2) {
113557
113720
  function detectEcosystems(directory) {
113558
113721
  const ecosystems = [];
113559
113722
  const cwd = directory;
113560
- if (fs91.existsSync(path128.join(cwd, "package.json"))) {
113723
+ if (fs96.existsSync(path133.join(cwd, "package.json"))) {
113561
113724
  ecosystems.push("npm");
113562
113725
  }
113563
- if (fs91.existsSync(path128.join(cwd, "pyproject.toml")) || fs91.existsSync(path128.join(cwd, "requirements.txt"))) {
113726
+ if (fs96.existsSync(path133.join(cwd, "pyproject.toml")) || fs96.existsSync(path133.join(cwd, "requirements.txt"))) {
113564
113727
  ecosystems.push("pip");
113565
113728
  }
113566
- if (fs91.existsSync(path128.join(cwd, "Cargo.toml"))) {
113729
+ if (fs96.existsSync(path133.join(cwd, "Cargo.toml"))) {
113567
113730
  ecosystems.push("cargo");
113568
113731
  }
113569
- if (fs91.existsSync(path128.join(cwd, "go.mod"))) {
113732
+ if (fs96.existsSync(path133.join(cwd, "go.mod"))) {
113570
113733
  ecosystems.push("go");
113571
113734
  }
113572
113735
  try {
113573
- const files = fs91.readdirSync(cwd);
113736
+ const files = fs96.readdirSync(cwd);
113574
113737
  if (files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"))) {
113575
113738
  ecosystems.push("dotnet");
113576
113739
  }
113577
113740
  } catch {}
113578
- if (fs91.existsSync(path128.join(cwd, "Gemfile")) || fs91.existsSync(path128.join(cwd, "Gemfile.lock"))) {
113741
+ if (fs96.existsSync(path133.join(cwd, "Gemfile")) || fs96.existsSync(path133.join(cwd, "Gemfile.lock"))) {
113579
113742
  ecosystems.push("ruby");
113580
113743
  }
113581
- if (fs91.existsSync(path128.join(cwd, "pubspec.yaml"))) {
113744
+ if (fs96.existsSync(path133.join(cwd, "pubspec.yaml"))) {
113582
113745
  ecosystems.push("dart");
113583
113746
  }
113584
- if (fs91.existsSync(path128.join(cwd, "composer.lock"))) {
113747
+ if (fs96.existsSync(path133.join(cwd, "composer.lock"))) {
113585
113748
  ecosystems.push("composer");
113586
113749
  }
113587
113750
  return ecosystems;
@@ -113613,8 +113776,8 @@ async function runNpmAudit(directory) {
113613
113776
  };
113614
113777
  }
113615
113778
  let { stdout, stderr } = result;
113616
- if (stdout.length > MAX_OUTPUT_BYTES5) {
113617
- stdout = stdout.slice(0, MAX_OUTPUT_BYTES5);
113779
+ if (stdout.length > MAX_OUTPUT_BYTES6) {
113780
+ stdout = stdout.slice(0, MAX_OUTPUT_BYTES6);
113618
113781
  }
113619
113782
  const exitCode = await proc.exited;
113620
113783
  if (exitCode === 0) {
@@ -113733,8 +113896,8 @@ async function runPipAudit(directory) {
113733
113896
  };
113734
113897
  }
113735
113898
  let { stdout, stderr } = result;
113736
- if (stdout.length > MAX_OUTPUT_BYTES5) {
113737
- stdout = stdout.slice(0, MAX_OUTPUT_BYTES5);
113899
+ if (stdout.length > MAX_OUTPUT_BYTES6) {
113900
+ stdout = stdout.slice(0, MAX_OUTPUT_BYTES6);
113738
113901
  }
113739
113902
  const exitCode = await proc.exited;
113740
113903
  if (exitCode === 0 && !stdout.trim()) {
@@ -113861,8 +114024,8 @@ async function runCargoAudit(directory) {
113861
114024
  };
113862
114025
  }
113863
114026
  let { stdout, stderr: _stderr } = result;
113864
- if (stdout.length > MAX_OUTPUT_BYTES5) {
113865
- stdout = stdout.slice(0, MAX_OUTPUT_BYTES5);
114027
+ if (stdout.length > MAX_OUTPUT_BYTES6) {
114028
+ stdout = stdout.slice(0, MAX_OUTPUT_BYTES6);
113866
114029
  }
113867
114030
  const exitCode = await proc.exited;
113868
114031
  if (exitCode === 0) {
@@ -113985,8 +114148,8 @@ async function runGoAudit(directory) {
113985
114148
  };
113986
114149
  }
113987
114150
  let { stdout } = result;
113988
- if (stdout.length > MAX_OUTPUT_BYTES5) {
113989
- stdout = stdout.slice(0, MAX_OUTPUT_BYTES5);
114151
+ if (stdout.length > MAX_OUTPUT_BYTES6) {
114152
+ stdout = stdout.slice(0, MAX_OUTPUT_BYTES6);
113990
114153
  }
113991
114154
  const exitCode = await proc.exited;
113992
114155
  if (exitCode !== 0 && exitCode !== 3) {
@@ -114118,8 +114281,8 @@ async function runDotnetAudit(directory) {
114118
114281
  };
114119
114282
  }
114120
114283
  let { stdout } = result;
114121
- if (stdout.length > MAX_OUTPUT_BYTES5) {
114122
- stdout = stdout.slice(0, MAX_OUTPUT_BYTES5);
114284
+ if (stdout.length > MAX_OUTPUT_BYTES6) {
114285
+ stdout = stdout.slice(0, MAX_OUTPUT_BYTES6);
114123
114286
  }
114124
114287
  const exitCode = await proc.exited;
114125
114288
  if (exitCode !== 0 && !stdout.includes("has the following vulnerable packages")) {
@@ -114234,8 +114397,8 @@ async function runBundleAudit(directory) {
114234
114397
  };
114235
114398
  }
114236
114399
  let { stdout } = result;
114237
- if (stdout.length > MAX_OUTPUT_BYTES5) {
114238
- stdout = stdout.slice(0, MAX_OUTPUT_BYTES5);
114400
+ if (stdout.length > MAX_OUTPUT_BYTES6) {
114401
+ stdout = stdout.slice(0, MAX_OUTPUT_BYTES6);
114239
114402
  }
114240
114403
  const exitCode = await proc.exited;
114241
114404
  if (exitCode !== 0 && exitCode !== 1) {
@@ -114379,8 +114542,8 @@ async function runDartAudit(directory) {
114379
114542
  };
114380
114543
  }
114381
114544
  let { stdout } = result;
114382
- if (stdout.length > MAX_OUTPUT_BYTES5) {
114383
- stdout = stdout.slice(0, MAX_OUTPUT_BYTES5);
114545
+ if (stdout.length > MAX_OUTPUT_BYTES6) {
114546
+ stdout = stdout.slice(0, MAX_OUTPUT_BYTES6);
114384
114547
  }
114385
114548
  const exitCode = await proc.exited;
114386
114549
  if (exitCode !== 0) {
@@ -114494,8 +114657,8 @@ async function runComposerAudit(directory) {
114494
114657
  };
114495
114658
  }
114496
114659
  let { stdout } = result;
114497
- if (stdout.length > MAX_OUTPUT_BYTES5) {
114498
- stdout = stdout.slice(0, MAX_OUTPUT_BYTES5);
114660
+ if (stdout.length > MAX_OUTPUT_BYTES6) {
114661
+ stdout = stdout.slice(0, MAX_OUTPUT_BYTES6);
114499
114662
  }
114500
114663
  const exitCode = await proc.exited;
114501
114664
  const hasVulnerabilities = (exitCode & 1) !== 0;
@@ -114716,8 +114879,8 @@ var pkg_audit = createSwarmTool({
114716
114879
  // src/tools/placeholder-scan.ts
114717
114880
  init_zod();
114718
114881
  init_manager2();
114719
- import * as fs92 from "node:fs";
114720
- import * as path129 from "node:path";
114882
+ import * as fs97 from "node:fs";
114883
+ import * as path134 from "node:path";
114721
114884
  init_utils();
114722
114885
  init_create_tool();
114723
114886
  var MAX_FILE_SIZE = 1024 * 1024;
@@ -114840,7 +115003,7 @@ function isScaffoldFile(filePath) {
114840
115003
  if (SCAFFOLD_PATH_PATTERNS.some((pattern) => pattern.test(normalizedPath))) {
114841
115004
  return true;
114842
115005
  }
114843
- const filename = path129.basename(filePath);
115006
+ const filename = path134.basename(filePath);
114844
115007
  if (SCAFFOLD_FILENAME_PATTERNS.some((pattern) => pattern.test(filename))) {
114845
115008
  return true;
114846
115009
  }
@@ -114857,7 +115020,7 @@ function isAllowedByGlobs(filePath, allowGlobs) {
114857
115020
  if (regex.test(normalizedPath)) {
114858
115021
  return true;
114859
115022
  }
114860
- const filename = path129.basename(filePath);
115023
+ const filename = path134.basename(filePath);
114861
115024
  const filenameRegex = new RegExp(`^${regexPattern}$`, "i");
114862
115025
  if (filenameRegex.test(filename)) {
114863
115026
  return true;
@@ -114866,7 +115029,7 @@ function isAllowedByGlobs(filePath, allowGlobs) {
114866
115029
  return false;
114867
115030
  }
114868
115031
  function isParserSupported(filePath) {
114869
- const ext = path129.extname(filePath).toLowerCase();
115032
+ const ext = path134.extname(filePath).toLowerCase();
114870
115033
  return SUPPORTED_PARSER_EXTENSIONS.has(ext);
114871
115034
  }
114872
115035
  function isPlanFile(filePath) {
@@ -115113,28 +115276,28 @@ async function placeholderScan(input, directory) {
115113
115276
  let filesScanned = 0;
115114
115277
  const filesWithFindings = new Set;
115115
115278
  for (const filePath of changed_files) {
115116
- const fullPath = path129.isAbsolute(filePath) ? filePath : path129.resolve(directory, filePath);
115117
- const resolvedDirectory = path129.resolve(directory);
115118
- if (!fullPath.startsWith(resolvedDirectory + path129.sep) && fullPath !== resolvedDirectory) {
115279
+ const fullPath = path134.isAbsolute(filePath) ? filePath : path134.resolve(directory, filePath);
115280
+ const resolvedDirectory = path134.resolve(directory);
115281
+ if (!fullPath.startsWith(resolvedDirectory + path134.sep) && fullPath !== resolvedDirectory) {
115119
115282
  continue;
115120
115283
  }
115121
- if (!fs92.existsSync(fullPath)) {
115284
+ if (!fs97.existsSync(fullPath)) {
115122
115285
  continue;
115123
115286
  }
115124
115287
  if (isAllowedByGlobs(filePath, allow_globs)) {
115125
115288
  continue;
115126
115289
  }
115127
- const relativeFilePath = path129.relative(directory, fullPath).replace(/\\/g, "/");
115290
+ const relativeFilePath = path134.relative(directory, fullPath).replace(/\\/g, "/");
115128
115291
  if (FILE_ALLOWLIST.some((allowed) => relativeFilePath.endsWith(allowed))) {
115129
115292
  continue;
115130
115293
  }
115131
115294
  let content;
115132
115295
  try {
115133
- const stat9 = fs92.statSync(fullPath);
115296
+ const stat9 = fs97.statSync(fullPath);
115134
115297
  if (stat9.size > MAX_FILE_SIZE) {
115135
115298
  continue;
115136
115299
  }
115137
- content = fs92.readFileSync(fullPath, "utf-8");
115300
+ content = fs97.readFileSync(fullPath, "utf-8");
115138
115301
  } catch {
115139
115302
  continue;
115140
115303
  }
@@ -115195,8 +115358,8 @@ var placeholder_scan = createSwarmTool({
115195
115358
  }
115196
115359
  });
115197
115360
  // src/tools/pre-check-batch.ts
115198
- import * as fs96 from "node:fs";
115199
- import * as path133 from "node:path";
115361
+ import * as fs101 from "node:fs";
115362
+ import * as path138 from "node:path";
115200
115363
  init_zod();
115201
115364
  init_manager2();
115202
115365
  init_utils();
@@ -115336,8 +115499,8 @@ var _internals56 = {
115336
115499
  init_zod();
115337
115500
  init_manager2();
115338
115501
  init_detector();
115339
- import * as fs95 from "node:fs";
115340
- import * as path132 from "node:path";
115502
+ import * as fs100 from "node:fs";
115503
+ import * as path137 from "node:path";
115341
115504
  import { extname as extname20 } from "node:path";
115342
115505
 
115343
115506
  // src/sast/rules/c.ts
@@ -116052,8 +116215,8 @@ function executeRulesSync(filePath, content, language) {
116052
116215
 
116053
116216
  // src/sast/semgrep.ts
116054
116217
  import * as child_process9 from "node:child_process";
116055
- import * as fs93 from "node:fs";
116056
- import * as path130 from "node:path";
116218
+ import * as fs98 from "node:fs";
116219
+ import * as path135 from "node:path";
116057
116220
  var semgrepAvailableCache = null;
116058
116221
  var DEFAULT_RULES_DIR = ".swarm/semgrep-rules";
116059
116222
  var DEFAULT_TIMEOUT_MS3 = 30000;
@@ -116240,14 +116403,14 @@ async function runSemgrep(options) {
116240
116403
  }
116241
116404
  function getRulesDirectory(projectRoot) {
116242
116405
  if (projectRoot) {
116243
- return path130.resolve(projectRoot, DEFAULT_RULES_DIR);
116406
+ return path135.resolve(projectRoot, DEFAULT_RULES_DIR);
116244
116407
  }
116245
116408
  return DEFAULT_RULES_DIR;
116246
116409
  }
116247
116410
  function hasBundledRules(projectRoot) {
116248
116411
  const rulesDir = getRulesDirectory(projectRoot);
116249
116412
  try {
116250
- return fs93.existsSync(rulesDir);
116413
+ return fs98.existsSync(rulesDir);
116251
116414
  } catch {
116252
116415
  return false;
116253
116416
  }
@@ -116260,25 +116423,25 @@ init_create_tool();
116260
116423
  // src/tools/sast-baseline.ts
116261
116424
  init_utils2();
116262
116425
  import * as crypto11 from "node:crypto";
116263
- import * as fs94 from "node:fs";
116264
- import * as path131 from "node:path";
116426
+ import * as fs99 from "node:fs";
116427
+ import * as path136 from "node:path";
116265
116428
  var BASELINE_SCHEMA_VERSION = "1.0.0";
116266
116429
  var MAX_BASELINE_FINDINGS = 2000;
116267
116430
  var MAX_BASELINE_BYTES = 2 * 1048576;
116268
116431
  var LOCK_RETRY_DELAYS_MS = [50, 100, 200, 400, 800];
116269
116432
  function normalizeFindingPath(directory, file3) {
116270
- const resolved = path131.isAbsolute(file3) ? file3 : path131.resolve(directory, file3);
116271
- const rel = path131.relative(path131.resolve(directory), resolved);
116433
+ const resolved = path136.isAbsolute(file3) ? file3 : path136.resolve(directory, file3);
116434
+ const rel = path136.relative(path136.resolve(directory), resolved);
116272
116435
  return rel.replace(/\\/g, "/");
116273
116436
  }
116274
116437
  function baselineRelPath(phase) {
116275
- return path131.join("evidence", String(phase), "sast-baseline.json");
116438
+ return path136.join("evidence", String(phase), "sast-baseline.json");
116276
116439
  }
116277
116440
  function tempRelPath(phase) {
116278
- return path131.join("evidence", String(phase), `sast-baseline.json.tmp.${Date.now()}.${process.pid}`);
116441
+ return path136.join("evidence", String(phase), `sast-baseline.json.tmp.${Date.now()}.${process.pid}`);
116279
116442
  }
116280
116443
  function lockRelPath(phase) {
116281
- return path131.join("evidence", String(phase), "sast-baseline.json.lock");
116444
+ return path136.join("evidence", String(phase), "sast-baseline.json.lock");
116282
116445
  }
116283
116446
  function getLine(lines, idx) {
116284
116447
  if (idx < 0 || idx >= lines.length)
@@ -116295,7 +116458,7 @@ function fingerprintFinding(finding, directory, occurrenceIndex) {
116295
116458
  }
116296
116459
  const lineNum = finding.location.line;
116297
116460
  try {
116298
- const content = fs94.readFileSync(finding.location.file, "utf-8");
116461
+ const content = fs99.readFileSync(finding.location.file, "utf-8");
116299
116462
  const lines = content.split(`
116300
116463
  `);
116301
116464
  const idx = lineNum - 1;
@@ -116326,7 +116489,7 @@ function assignOccurrenceIndices(findings, directory) {
116326
116489
  try {
116327
116490
  if (relFile.startsWith(".."))
116328
116491
  throw new Error("escapes workspace");
116329
- const content = fs94.readFileSync(finding.location.file, "utf-8");
116492
+ const content = fs99.readFileSync(finding.location.file, "utf-8");
116330
116493
  const lines = content.split(`
116331
116494
  `);
116332
116495
  const idx = lineNum - 1;
@@ -116355,11 +116518,11 @@ function assignOccurrenceIndices(findings, directory) {
116355
116518
  async function acquireLock2(lockPath) {
116356
116519
  for (let attempt = 0;attempt <= LOCK_RETRY_DELAYS_MS.length; attempt++) {
116357
116520
  try {
116358
- const fd = fs94.openSync(lockPath, "wx");
116359
- fs94.closeSync(fd);
116521
+ const fd = fs99.openSync(lockPath, "wx");
116522
+ fs99.closeSync(fd);
116360
116523
  return () => {
116361
116524
  try {
116362
- fs94.unlinkSync(lockPath);
116525
+ fs99.unlinkSync(lockPath);
116363
116526
  } catch {}
116364
116527
  };
116365
116528
  } catch {
@@ -116399,13 +116562,13 @@ async function captureOrMergeBaseline(directory, phase, findings, engine, scanne
116399
116562
  message: e instanceof Error ? e.message : "Path validation failed"
116400
116563
  };
116401
116564
  }
116402
- fs94.mkdirSync(path131.dirname(baselinePath), { recursive: true });
116403
- fs94.mkdirSync(path131.dirname(tempPath), { recursive: true });
116565
+ fs99.mkdirSync(path136.dirname(baselinePath), { recursive: true });
116566
+ fs99.mkdirSync(path136.dirname(tempPath), { recursive: true });
116404
116567
  const releaseLock = await acquireLock2(lockPath);
116405
116568
  try {
116406
116569
  let existing = null;
116407
116570
  try {
116408
- const raw = fs94.readFileSync(baselinePath, "utf-8");
116571
+ const raw = fs99.readFileSync(baselinePath, "utf-8");
116409
116572
  const parsed = JSON.parse(raw);
116410
116573
  if (parsed.schema_version === BASELINE_SCHEMA_VERSION) {
116411
116574
  existing = parsed;
@@ -116465,8 +116628,8 @@ async function captureOrMergeBaseline(directory, phase, findings, engine, scanne
116465
116628
  message: `Baseline would exceed size cap (${json4.length} bytes > ${MAX_BASELINE_BYTES})`
116466
116629
  };
116467
116630
  }
116468
- fs94.writeFileSync(tempPath, json4, "utf-8");
116469
- fs94.renameSync(tempPath, baselinePath);
116631
+ fs99.writeFileSync(tempPath, json4, "utf-8");
116632
+ fs99.renameSync(tempPath, baselinePath);
116470
116633
  return {
116471
116634
  status: "merged",
116472
116635
  path: baselinePath,
@@ -116497,8 +116660,8 @@ async function captureOrMergeBaseline(directory, phase, findings, engine, scanne
116497
116660
  message: `Baseline would exceed size cap (${json3.length} bytes > ${MAX_BASELINE_BYTES})`
116498
116661
  };
116499
116662
  }
116500
- fs94.writeFileSync(tempPath, json3, "utf-8");
116501
- fs94.renameSync(tempPath, baselinePath);
116663
+ fs99.writeFileSync(tempPath, json3, "utf-8");
116664
+ fs99.renameSync(tempPath, baselinePath);
116502
116665
  return {
116503
116666
  status: "written",
116504
116667
  path: baselinePath,
@@ -116523,7 +116686,7 @@ function loadBaseline(directory, phase) {
116523
116686
  };
116524
116687
  }
116525
116688
  try {
116526
- const raw = fs94.readFileSync(baselinePath, "utf-8");
116689
+ const raw = fs99.readFileSync(baselinePath, "utf-8");
116527
116690
  const parsed = JSON.parse(raw);
116528
116691
  if (parsed.schema_version !== BASELINE_SCHEMA_VERSION) {
116529
116692
  return {
@@ -116571,17 +116734,17 @@ var SEVERITY_ORDER = {
116571
116734
  };
116572
116735
  function shouldSkipFile(filePath) {
116573
116736
  try {
116574
- const stats = fs95.statSync(filePath);
116737
+ const stats = fs100.statSync(filePath);
116575
116738
  if (stats.size > MAX_FILE_SIZE_BYTES8) {
116576
116739
  return { skip: true, reason: "file too large" };
116577
116740
  }
116578
116741
  if (stats.size === 0) {
116579
116742
  return { skip: true, reason: "empty file" };
116580
116743
  }
116581
- const fd = fs95.openSync(filePath, "r");
116744
+ const fd = fs100.openSync(filePath, "r");
116582
116745
  const buffer = Buffer.alloc(8192);
116583
- const bytesRead = fs95.readSync(fd, buffer, 0, 8192, 0);
116584
- fs95.closeSync(fd);
116746
+ const bytesRead = fs100.readSync(fd, buffer, 0, 8192, 0);
116747
+ fs100.closeSync(fd);
116585
116748
  if (bytesRead > 0) {
116586
116749
  let nullCount = 0;
116587
116750
  for (let i2 = 0;i2 < bytesRead; i2++) {
@@ -116620,7 +116783,7 @@ function countBySeverity(findings) {
116620
116783
  }
116621
116784
  function scanFileWithTierA(filePath, language) {
116622
116785
  try {
116623
- const content = fs95.readFileSync(filePath, "utf-8");
116786
+ const content = fs100.readFileSync(filePath, "utf-8");
116624
116787
  const findings = executeRulesSync(filePath, content, language);
116625
116788
  return findings.map((f) => ({
116626
116789
  rule_id: f.rule_id,
@@ -116673,13 +116836,13 @@ async function sastScan(input, directory, config3) {
116673
116836
  _filesSkipped++;
116674
116837
  continue;
116675
116838
  }
116676
- const resolvedPath = path132.isAbsolute(filePath) ? filePath : path132.resolve(directory, filePath);
116677
- const resolvedDirectory = path132.resolve(directory);
116678
- if (!resolvedPath.startsWith(resolvedDirectory + path132.sep) && resolvedPath !== resolvedDirectory) {
116839
+ const resolvedPath = path137.isAbsolute(filePath) ? filePath : path137.resolve(directory, filePath);
116840
+ const resolvedDirectory = path137.resolve(directory);
116841
+ if (!resolvedPath.startsWith(resolvedDirectory + path137.sep) && resolvedPath !== resolvedDirectory) {
116679
116842
  _filesSkipped++;
116680
116843
  continue;
116681
116844
  }
116682
- if (!fs95.existsSync(resolvedPath)) {
116845
+ if (!fs100.existsSync(resolvedPath)) {
116683
116846
  _filesSkipped++;
116684
116847
  continue;
116685
116848
  }
@@ -116990,18 +117153,18 @@ function validatePath(inputPath, baseDir, workspaceDir) {
116990
117153
  let resolved;
116991
117154
  const isWinAbs = isWindowsAbsolutePath(inputPath);
116992
117155
  if (isWinAbs) {
116993
- resolved = path133.win32.resolve(inputPath);
116994
- } else if (path133.isAbsolute(inputPath)) {
116995
- resolved = path133.resolve(inputPath);
117156
+ resolved = path138.win32.resolve(inputPath);
117157
+ } else if (path138.isAbsolute(inputPath)) {
117158
+ resolved = path138.resolve(inputPath);
116996
117159
  } else {
116997
- resolved = path133.resolve(baseDir, inputPath);
117160
+ resolved = path138.resolve(baseDir, inputPath);
116998
117161
  }
116999
- const workspaceResolved = path133.resolve(workspaceDir);
117162
+ const workspaceResolved = path138.resolve(workspaceDir);
117000
117163
  let relative27;
117001
117164
  if (isWinAbs) {
117002
- relative27 = path133.win32.relative(workspaceResolved, resolved);
117165
+ relative27 = path138.win32.relative(workspaceResolved, resolved);
117003
117166
  } else {
117004
- relative27 = path133.relative(workspaceResolved, resolved);
117167
+ relative27 = path138.relative(workspaceResolved, resolved);
117005
117168
  }
117006
117169
  if (relative27.startsWith("..")) {
117007
117170
  return "path traversal detected";
@@ -117066,7 +117229,7 @@ async function runLintOnFiles(linter, files, workspaceDir) {
117066
117229
  if (typeof file3 !== "string") {
117067
117230
  continue;
117068
117231
  }
117069
- const resolvedPath = path133.resolve(file3);
117232
+ const resolvedPath = path138.resolve(file3);
117070
117233
  const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
117071
117234
  if (validationError) {
117072
117235
  continue;
@@ -117223,7 +117386,7 @@ async function runSecretscanWithFiles(files, directory) {
117223
117386
  skippedFiles++;
117224
117387
  continue;
117225
117388
  }
117226
- const resolvedPath = path133.resolve(file3);
117389
+ const resolvedPath = path138.resolve(file3);
117227
117390
  const validationError = validatePath(resolvedPath, directory, directory);
117228
117391
  if (validationError) {
117229
117392
  skippedFiles++;
@@ -117241,14 +117404,14 @@ async function runSecretscanWithFiles(files, directory) {
117241
117404
  };
117242
117405
  }
117243
117406
  for (const file3 of validatedFiles) {
117244
- const ext = path133.extname(file3).toLowerCase();
117407
+ const ext = path138.extname(file3).toLowerCase();
117245
117408
  if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
117246
117409
  skippedFiles++;
117247
117410
  continue;
117248
117411
  }
117249
117412
  let stat9;
117250
117413
  try {
117251
- stat9 = fs96.statSync(file3);
117414
+ stat9 = fs101.statSync(file3);
117252
117415
  } catch {
117253
117416
  skippedFiles++;
117254
117417
  continue;
@@ -117259,7 +117422,7 @@ async function runSecretscanWithFiles(files, directory) {
117259
117422
  }
117260
117423
  let content;
117261
117424
  try {
117262
- const buffer = fs96.readFileSync(file3);
117425
+ const buffer = fs101.readFileSync(file3);
117263
117426
  if (buffer.includes(0)) {
117264
117427
  skippedFiles++;
117265
117428
  continue;
@@ -117460,7 +117623,7 @@ function classifySastFindings(findings, changedLineRanges, directory) {
117460
117623
  const preexistingFindings = [];
117461
117624
  for (const finding of findings) {
117462
117625
  const filePath = finding.location.file;
117463
- const normalised = path133.relative(directory, filePath).replace(/\\/g, "/");
117626
+ const normalised = path138.relative(directory, filePath).replace(/\\/g, "/");
117464
117627
  const changedLines = changedLineRanges.get(normalised);
117465
117628
  if (changedLines?.has(finding.location.line)) {
117466
117629
  newFindings.push(finding);
@@ -117511,7 +117674,7 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
117511
117674
  warn(`pre_check_batch: Invalid file path: ${file3}`);
117512
117675
  continue;
117513
117676
  }
117514
- changedFiles.push(path133.resolve(directory, file3));
117677
+ changedFiles.push(path138.resolve(directory, file3));
117515
117678
  }
117516
117679
  if (changedFiles.length === 0) {
117517
117680
  warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
@@ -117712,9 +117875,9 @@ var pre_check_batch = createSwarmTool({
117712
117875
  };
117713
117876
  return JSON.stringify(errorResult, null, 2);
117714
117877
  }
117715
- const resolvedDirectory = path133.resolve(typedArgs.directory);
117716
- const workspaceAnchor = path133.resolve(directory);
117717
- if (resolvedDirectory !== workspaceAnchor && resolvedDirectory.startsWith(workspaceAnchor + path133.sep)) {
117878
+ const resolvedDirectory = path138.resolve(typedArgs.directory);
117879
+ const workspaceAnchor = path138.resolve(directory);
117880
+ if (resolvedDirectory !== workspaceAnchor && resolvedDirectory.startsWith(workspaceAnchor + path138.sep)) {
117718
117881
  const subDirError = `directory "${typedArgs.directory}" is a subdirectory of the project root — pre_check_batch requires the project root directory "${workspaceAnchor}"`;
117719
117882
  const subDirResult = {
117720
117883
  gates_passed: false,
@@ -117765,7 +117928,7 @@ var pre_check_batch = createSwarmTool({
117765
117928
  });
117766
117929
  // src/tools/repo-map.ts
117767
117930
  init_zod();
117768
- import * as path134 from "node:path";
117931
+ import * as path139 from "node:path";
117769
117932
  init_path_security();
117770
117933
  init_create_tool();
117771
117934
  var VALID_ACTIONS = [
@@ -117790,7 +117953,7 @@ function validateFile(p) {
117790
117953
  return "file contains control characters";
117791
117954
  if (containsPathTraversal(p))
117792
117955
  return "file contains path traversal";
117793
- if (path134.isAbsolute(p) || /^[a-zA-Z]:[\\/]/.test(p)) {
117956
+ if (path139.isAbsolute(p) || /^[a-zA-Z]:[\\/]/.test(p)) {
117794
117957
  return "file must be a workspace-relative path, not absolute";
117795
117958
  }
117796
117959
  return null;
@@ -117813,8 +117976,8 @@ function ok(action, payload) {
117813
117976
  }
117814
117977
  function toRelativeGraphPath(input, workspaceRoot) {
117815
117978
  const normalized = input.replace(/\\/g, "/");
117816
- if (path134.isAbsolute(normalized)) {
117817
- const rel = path134.relative(workspaceRoot, normalized).replace(/\\/g, "/");
117979
+ if (path139.isAbsolute(normalized)) {
117980
+ const rel = path139.relative(workspaceRoot, normalized).replace(/\\/g, "/");
117818
117981
  return normalizeGraphPath2(rel);
117819
117982
  }
117820
117983
  return normalizeGraphPath2(normalized);
@@ -117958,8 +118121,8 @@ var repo_map = createSwarmTool({
117958
118121
  // src/tools/req-coverage.ts
117959
118122
  init_zod();
117960
118123
  init_create_tool();
117961
- import * as fs97 from "node:fs";
117962
- import * as path135 from "node:path";
118124
+ import * as fs102 from "node:fs";
118125
+ import * as path140 from "node:path";
117963
118126
  var SPEC_FILE = ".swarm/spec.md";
117964
118127
  var EVIDENCE_DIR4 = ".swarm/evidence";
117965
118128
  var OBLIGATION_KEYWORDS = ["MUST", "SHOULD", "SHALL"];
@@ -118018,19 +118181,19 @@ function extractObligationAndText(id, lineText) {
118018
118181
  var PHASE_TASK_ID_REGEX = /^\d+\.\d+(\.\d+)*$/;
118019
118182
  function readTouchedFiles(evidenceDir, phase, cwd) {
118020
118183
  const touchedFiles = new Set;
118021
- if (!fs97.existsSync(evidenceDir) || !fs97.statSync(evidenceDir).isDirectory()) {
118184
+ if (!fs102.existsSync(evidenceDir) || !fs102.statSync(evidenceDir).isDirectory()) {
118022
118185
  return [];
118023
118186
  }
118024
118187
  let entries;
118025
118188
  try {
118026
- entries = fs97.readdirSync(evidenceDir);
118189
+ entries = fs102.readdirSync(evidenceDir);
118027
118190
  } catch {
118028
118191
  return [];
118029
118192
  }
118030
118193
  for (const entry of entries) {
118031
- const entryPath = path135.join(evidenceDir, entry);
118194
+ const entryPath = path140.join(evidenceDir, entry);
118032
118195
  try {
118033
- const stat9 = fs97.statSync(entryPath);
118196
+ const stat9 = fs102.statSync(entryPath);
118034
118197
  if (!stat9.isDirectory()) {
118035
118198
  continue;
118036
118199
  }
@@ -118044,14 +118207,14 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
118044
118207
  if (entryPhase !== String(phase)) {
118045
118208
  continue;
118046
118209
  }
118047
- const evidenceFilePath = path135.join(entryPath, "evidence.json");
118210
+ const evidenceFilePath = path140.join(entryPath, "evidence.json");
118048
118211
  try {
118049
- const resolvedPath = path135.resolve(evidenceFilePath);
118050
- const evidenceDirResolved = path135.resolve(evidenceDir);
118051
- if (!resolvedPath.startsWith(evidenceDirResolved + path135.sep)) {
118212
+ const resolvedPath = path140.resolve(evidenceFilePath);
118213
+ const evidenceDirResolved = path140.resolve(evidenceDir);
118214
+ if (!resolvedPath.startsWith(evidenceDirResolved + path140.sep)) {
118052
118215
  continue;
118053
118216
  }
118054
- const stat9 = fs97.lstatSync(evidenceFilePath);
118217
+ const stat9 = fs102.lstatSync(evidenceFilePath);
118055
118218
  if (!stat9.isFile()) {
118056
118219
  continue;
118057
118220
  }
@@ -118063,7 +118226,7 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
118063
118226
  }
118064
118227
  let content;
118065
118228
  try {
118066
- content = fs97.readFileSync(evidenceFilePath, "utf-8");
118229
+ content = fs102.readFileSync(evidenceFilePath, "utf-8");
118067
118230
  } catch {
118068
118231
  continue;
118069
118232
  }
@@ -118082,7 +118245,7 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
118082
118245
  if (Array.isArray(diffEntry.files_changed)) {
118083
118246
  for (const file3 of diffEntry.files_changed) {
118084
118247
  if (typeof file3 === "string") {
118085
- touchedFiles.add(path135.resolve(cwd, file3));
118248
+ touchedFiles.add(path140.resolve(cwd, file3));
118086
118249
  }
118087
118250
  }
118088
118251
  }
@@ -118095,12 +118258,12 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
118095
118258
  }
118096
118259
  function searchFileForKeywords(filePath, keywords, cwd) {
118097
118260
  try {
118098
- const resolvedPath = path135.resolve(filePath);
118099
- const cwdResolved = path135.resolve(cwd);
118261
+ const resolvedPath = path140.resolve(filePath);
118262
+ const cwdResolved = path140.resolve(cwd);
118100
118263
  if (!resolvedPath.startsWith(cwdResolved)) {
118101
118264
  return false;
118102
118265
  }
118103
- const content = fs97.readFileSync(resolvedPath, "utf-8");
118266
+ const content = fs102.readFileSync(resolvedPath, "utf-8");
118104
118267
  for (const keyword of keywords) {
118105
118268
  const regex = new RegExp(`\\b${keyword}\\b`, "i");
118106
118269
  if (regex.test(content)) {
@@ -118230,10 +118393,10 @@ var req_coverage = createSwarmTool({
118230
118393
  }, null, 2);
118231
118394
  }
118232
118395
  const cwd = inputDirectory || directory;
118233
- const specPath = path135.join(cwd, SPEC_FILE);
118396
+ const specPath = path140.join(cwd, SPEC_FILE);
118234
118397
  let specContent;
118235
118398
  try {
118236
- specContent = fs97.readFileSync(specPath, "utf-8");
118399
+ specContent = fs102.readFileSync(specPath, "utf-8");
118237
118400
  } catch (readError) {
118238
118401
  return JSON.stringify({
118239
118402
  success: false,
@@ -118257,7 +118420,7 @@ var req_coverage = createSwarmTool({
118257
118420
  message: "No FR requirements found in spec.md"
118258
118421
  }, null, 2);
118259
118422
  }
118260
- const evidenceDir = path135.join(cwd, EVIDENCE_DIR4);
118423
+ const evidenceDir = path140.join(cwd, EVIDENCE_DIR4);
118261
118424
  const touchedFiles = readTouchedFiles(evidenceDir, phase, cwd);
118262
118425
  const analyzedRequirements = [];
118263
118426
  let coveredCount = 0;
@@ -118283,12 +118446,12 @@ var req_coverage = createSwarmTool({
118283
118446
  requirements: analyzedRequirements
118284
118447
  };
118285
118448
  const reportFilename = `req-coverage-phase-${phase}.json`;
118286
- const reportPath = path135.join(evidenceDir, reportFilename);
118449
+ const reportPath = path140.join(evidenceDir, reportFilename);
118287
118450
  try {
118288
- if (!fs97.existsSync(evidenceDir)) {
118289
- fs97.mkdirSync(evidenceDir, { recursive: true });
118451
+ if (!fs102.existsSync(evidenceDir)) {
118452
+ fs102.mkdirSync(evidenceDir, { recursive: true });
118290
118453
  }
118291
- fs97.writeFileSync(reportPath, JSON.stringify(result, null, 2), "utf-8");
118454
+ fs102.writeFileSync(reportPath, JSON.stringify(result, null, 2), "utf-8");
118292
118455
  } catch (writeError) {
118293
118456
  console.warn(`Failed to write coverage report: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
118294
118457
  }
@@ -118370,8 +118533,8 @@ init_plan_schema();
118370
118533
  init_qa_gate_profile();
118371
118534
  init_file_locks();
118372
118535
  import * as crypto12 from "node:crypto";
118373
- import * as fs98 from "node:fs";
118374
- import * as path136 from "node:path";
118536
+ import * as fs103 from "node:fs";
118537
+ import * as path141 from "node:path";
118375
118538
  init_ledger();
118376
118539
  init_manager();
118377
118540
  init_state();
@@ -118452,17 +118615,17 @@ async function executeSavePlan(args2, fallbackDir) {
118452
118615
  };
118453
118616
  }
118454
118617
  if (args2.working_directory && fallbackDir) {
118455
- const resolvedTarget = path136.resolve(args2.working_directory);
118456
- const resolvedRoot = path136.resolve(fallbackDir);
118618
+ const resolvedTarget = path141.resolve(args2.working_directory);
118619
+ const resolvedRoot = path141.resolve(fallbackDir);
118457
118620
  let fallbackExists = false;
118458
118621
  try {
118459
- fs98.accessSync(resolvedRoot, fs98.constants.F_OK);
118622
+ fs103.accessSync(resolvedRoot, fs103.constants.F_OK);
118460
118623
  fallbackExists = true;
118461
118624
  } catch {
118462
118625
  fallbackExists = false;
118463
118626
  }
118464
118627
  if (fallbackExists) {
118465
- const isSubdirectory = resolvedTarget.startsWith(resolvedRoot + path136.sep);
118628
+ const isSubdirectory = resolvedTarget.startsWith(resolvedRoot + path141.sep);
118466
118629
  if (isSubdirectory) {
118467
118630
  return {
118468
118631
  success: false,
@@ -118478,11 +118641,11 @@ async function executeSavePlan(args2, fallbackDir) {
118478
118641
  let specMtime;
118479
118642
  let specHash;
118480
118643
  if (process.env.SWARM_SKIP_SPEC_GATE !== "1") {
118481
- const specPath = path136.join(targetWorkspace, ".swarm", "spec.md");
118644
+ const specPath = path141.join(targetWorkspace, ".swarm", "spec.md");
118482
118645
  try {
118483
- const stat9 = await fs98.promises.stat(specPath);
118646
+ const stat9 = await fs103.promises.stat(specPath);
118484
118647
  specMtime = stat9.mtime.toISOString();
118485
- const content = await fs98.promises.readFile(specPath, "utf8");
118648
+ const content = await fs103.promises.readFile(specPath, "utf8");
118486
118649
  specHash = crypto12.createHash("sha256").update(content).digest("hex");
118487
118650
  } catch {
118488
118651
  return {
@@ -118494,10 +118657,10 @@ async function executeSavePlan(args2, fallbackDir) {
118494
118657
  }
118495
118658
  }
118496
118659
  if (process.env.SWARM_SKIP_GATE_SELECTION !== "1") {
118497
- const contextPath = path136.join(targetWorkspace, ".swarm", "context.md");
118660
+ const contextPath = path141.join(targetWorkspace, ".swarm", "context.md");
118498
118661
  let contextContent = "";
118499
118662
  try {
118500
- contextContent = await fs98.promises.readFile(contextPath, "utf8");
118663
+ contextContent = await fs103.promises.readFile(contextPath, "utf8");
118501
118664
  } catch {}
118502
118665
  const hasPendingSection = contextContent.includes("## Pending QA Gate Selection");
118503
118666
  if (!hasPendingSection) {
@@ -118784,14 +118947,14 @@ async function executeSavePlan(args2, fallbackDir) {
118784
118947
  }
118785
118948
  await writeCheckpoint(dir).catch(() => {});
118786
118949
  try {
118787
- const markerPath = path136.join(dir, ".swarm", ".plan-write-marker");
118950
+ const markerPath = path141.join(dir, ".swarm", ".plan-write-marker");
118788
118951
  const marker = JSON.stringify({
118789
118952
  source: "save_plan",
118790
118953
  timestamp: new Date().toISOString(),
118791
118954
  phases_count: plan.phases.length,
118792
118955
  tasks_count: tasksCount
118793
118956
  });
118794
- await fs98.promises.writeFile(markerPath, marker, "utf8");
118957
+ await fs103.promises.writeFile(markerPath, marker, "utf8");
118795
118958
  } catch {}
118796
118959
  const warnings = [];
118797
118960
  let criticReviewFound = false;
@@ -118807,7 +118970,7 @@ async function executeSavePlan(args2, fallbackDir) {
118807
118970
  return {
118808
118971
  success: true,
118809
118972
  message: "Plan saved successfully",
118810
- plan_path: path136.join(dir, ".swarm", "plan.json"),
118973
+ plan_path: path141.join(dir, ".swarm", "plan.json"),
118811
118974
  phases_count: plan.phases.length,
118812
118975
  tasks_count: tasksCount,
118813
118976
  ...resolvedProfile !== undefined ? { execution_profile: resolvedProfile } : {},
@@ -118872,8 +119035,8 @@ var save_plan = createSwarmTool({
118872
119035
  // src/tools/sbom-generate.ts
118873
119036
  init_zod();
118874
119037
  init_manager2();
118875
- import * as fs99 from "node:fs";
118876
- import * as path137 from "node:path";
119038
+ import * as fs104 from "node:fs";
119039
+ import * as path142 from "node:path";
118877
119040
 
118878
119041
  // src/sbom/detectors/index.ts
118879
119042
  init_utils();
@@ -119721,9 +119884,9 @@ function findManifestFiles(rootDir) {
119721
119884
  const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
119722
119885
  function searchDir(dir) {
119723
119886
  try {
119724
- const entries = fs99.readdirSync(dir, { withFileTypes: true });
119887
+ const entries = fs104.readdirSync(dir, { withFileTypes: true });
119725
119888
  for (const entry of entries) {
119726
- const fullPath = path137.join(dir, entry.name);
119889
+ const fullPath = path142.join(dir, entry.name);
119727
119890
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
119728
119891
  continue;
119729
119892
  }
@@ -119732,7 +119895,7 @@ function findManifestFiles(rootDir) {
119732
119895
  } else if (entry.isFile()) {
119733
119896
  for (const pattern of patterns) {
119734
119897
  if (simpleGlobToRegex(pattern).test(entry.name)) {
119735
- manifestFiles.push(path137.relative(rootDir, fullPath));
119898
+ manifestFiles.push(path142.relative(rootDir, fullPath));
119736
119899
  break;
119737
119900
  }
119738
119901
  }
@@ -119748,13 +119911,13 @@ function findManifestFilesInDirs(directories, workingDir) {
119748
119911
  const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
119749
119912
  for (const dir of directories) {
119750
119913
  try {
119751
- const entries = fs99.readdirSync(dir, { withFileTypes: true });
119914
+ const entries = fs104.readdirSync(dir, { withFileTypes: true });
119752
119915
  for (const entry of entries) {
119753
- const fullPath = path137.join(dir, entry.name);
119916
+ const fullPath = path142.join(dir, entry.name);
119754
119917
  if (entry.isFile()) {
119755
119918
  for (const pattern of patterns) {
119756
119919
  if (simpleGlobToRegex(pattern).test(entry.name)) {
119757
- found.push(path137.relative(workingDir, fullPath));
119920
+ found.push(path142.relative(workingDir, fullPath));
119758
119921
  break;
119759
119922
  }
119760
119923
  }
@@ -119767,11 +119930,11 @@ function findManifestFilesInDirs(directories, workingDir) {
119767
119930
  function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
119768
119931
  const dirs = new Set;
119769
119932
  for (const file3 of changedFiles) {
119770
- let currentDir = path137.dirname(file3);
119933
+ let currentDir = path142.dirname(file3);
119771
119934
  while (true) {
119772
- if (currentDir && currentDir !== "." && currentDir !== path137.sep) {
119773
- dirs.add(path137.join(workingDir, currentDir));
119774
- const parent = path137.dirname(currentDir);
119935
+ if (currentDir && currentDir !== "." && currentDir !== path142.sep) {
119936
+ dirs.add(path142.join(workingDir, currentDir));
119937
+ const parent = path142.dirname(currentDir);
119775
119938
  if (parent === currentDir)
119776
119939
  break;
119777
119940
  currentDir = parent;
@@ -119785,7 +119948,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
119785
119948
  }
119786
119949
  function ensureOutputDir(outputDir) {
119787
119950
  try {
119788
- fs99.mkdirSync(outputDir, { recursive: true });
119951
+ fs104.mkdirSync(outputDir, { recursive: true });
119789
119952
  } catch (error93) {
119790
119953
  if (!error93 || error93.code !== "EEXIST") {
119791
119954
  throw error93;
@@ -119855,7 +120018,7 @@ var sbom_generate = createSwarmTool({
119855
120018
  const changedFiles = obj.changed_files;
119856
120019
  const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
119857
120020
  const workingDir = directory;
119858
- const outputDir = path137.isAbsolute(relativeOutputDir) ? relativeOutputDir : path137.join(workingDir, relativeOutputDir);
120021
+ const outputDir = path142.isAbsolute(relativeOutputDir) ? relativeOutputDir : path142.join(workingDir, relativeOutputDir);
119859
120022
  let manifestFiles = [];
119860
120023
  if (scope === "all") {
119861
120024
  manifestFiles = findManifestFiles(workingDir);
@@ -119878,11 +120041,11 @@ var sbom_generate = createSwarmTool({
119878
120041
  const processedFiles = [];
119879
120042
  for (const manifestFile of manifestFiles) {
119880
120043
  try {
119881
- const fullPath = path137.isAbsolute(manifestFile) ? manifestFile : path137.join(workingDir, manifestFile);
119882
- if (!fs99.existsSync(fullPath)) {
120044
+ const fullPath = path142.isAbsolute(manifestFile) ? manifestFile : path142.join(workingDir, manifestFile);
120045
+ if (!fs104.existsSync(fullPath)) {
119883
120046
  continue;
119884
120047
  }
119885
- const content = fs99.readFileSync(fullPath, "utf-8");
120048
+ const content = fs104.readFileSync(fullPath, "utf-8");
119886
120049
  const components = detectComponents(manifestFile, content);
119887
120050
  processedFiles.push(manifestFile);
119888
120051
  if (components.length > 0) {
@@ -119895,8 +120058,8 @@ var sbom_generate = createSwarmTool({
119895
120058
  const bom = generateCycloneDX(allComponents);
119896
120059
  const bomJson = serializeCycloneDX(bom);
119897
120060
  const filename = generateSbomFilename();
119898
- const outputPath = path137.join(outputDir, filename);
119899
- fs99.writeFileSync(outputPath, bomJson, "utf-8");
120061
+ const outputPath = path142.join(outputDir, filename);
120062
+ fs104.writeFileSync(outputPath, bomJson, "utf-8");
119900
120063
  const verdict = processedFiles.length > 0 ? "pass" : "pass";
119901
120064
  try {
119902
120065
  const timestamp = new Date().toISOString();
@@ -119938,8 +120101,8 @@ var sbom_generate = createSwarmTool({
119938
120101
  // src/tools/schema-drift.ts
119939
120102
  init_zod();
119940
120103
  init_create_tool();
119941
- import * as fs100 from "node:fs";
119942
- import * as path138 from "node:path";
120104
+ import * as fs105 from "node:fs";
120105
+ import * as path143 from "node:path";
119943
120106
  var SPEC_CANDIDATES = [
119944
120107
  "openapi.json",
119945
120108
  "openapi.yaml",
@@ -119971,28 +120134,28 @@ function normalizePath4(p) {
119971
120134
  }
119972
120135
  function discoverSpecFile(cwd, specFileArg) {
119973
120136
  if (specFileArg) {
119974
- const resolvedPath = path138.resolve(cwd, specFileArg);
119975
- const normalizedCwd = cwd.endsWith(path138.sep) ? cwd : cwd + path138.sep;
120137
+ const resolvedPath = path143.resolve(cwd, specFileArg);
120138
+ const normalizedCwd = cwd.endsWith(path143.sep) ? cwd : cwd + path143.sep;
119976
120139
  if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
119977
120140
  throw new Error("Invalid spec_file: path traversal detected");
119978
120141
  }
119979
- const ext = path138.extname(resolvedPath).toLowerCase();
120142
+ const ext = path143.extname(resolvedPath).toLowerCase();
119980
120143
  if (!ALLOWED_EXTENSIONS.includes(ext)) {
119981
120144
  throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
119982
120145
  }
119983
- const stats = fs100.statSync(resolvedPath);
120146
+ const stats = fs105.statSync(resolvedPath);
119984
120147
  if (stats.size > MAX_SPEC_SIZE) {
119985
120148
  throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
119986
120149
  }
119987
- if (!fs100.existsSync(resolvedPath)) {
120150
+ if (!fs105.existsSync(resolvedPath)) {
119988
120151
  throw new Error(`Spec file not found: ${resolvedPath}`);
119989
120152
  }
119990
120153
  return resolvedPath;
119991
120154
  }
119992
120155
  for (const candidate of SPEC_CANDIDATES) {
119993
- const candidatePath = path138.resolve(cwd, candidate);
119994
- if (fs100.existsSync(candidatePath)) {
119995
- const stats = fs100.statSync(candidatePath);
120156
+ const candidatePath = path143.resolve(cwd, candidate);
120157
+ if (fs105.existsSync(candidatePath)) {
120158
+ const stats = fs105.statSync(candidatePath);
119996
120159
  if (stats.size <= MAX_SPEC_SIZE) {
119997
120160
  return candidatePath;
119998
120161
  }
@@ -120001,8 +120164,8 @@ function discoverSpecFile(cwd, specFileArg) {
120001
120164
  return null;
120002
120165
  }
120003
120166
  function parseSpec(specFile) {
120004
- const content = fs100.readFileSync(specFile, "utf-8");
120005
- const ext = path138.extname(specFile).toLowerCase();
120167
+ const content = fs105.readFileSync(specFile, "utf-8");
120168
+ const ext = path143.extname(specFile).toLowerCase();
120006
120169
  if (ext === ".json") {
120007
120170
  return parseJsonSpec(content);
120008
120171
  }
@@ -120073,12 +120236,12 @@ function extractRoutes(cwd) {
120073
120236
  function walkDir(dir) {
120074
120237
  let entries;
120075
120238
  try {
120076
- entries = fs100.readdirSync(dir, { withFileTypes: true });
120239
+ entries = fs105.readdirSync(dir, { withFileTypes: true });
120077
120240
  } catch {
120078
120241
  return;
120079
120242
  }
120080
120243
  for (const entry of entries) {
120081
- const fullPath = path138.join(dir, entry.name);
120244
+ const fullPath = path143.join(dir, entry.name);
120082
120245
  if (entry.isSymbolicLink()) {
120083
120246
  continue;
120084
120247
  }
@@ -120088,7 +120251,7 @@ function extractRoutes(cwd) {
120088
120251
  }
120089
120252
  walkDir(fullPath);
120090
120253
  } else if (entry.isFile()) {
120091
- const ext = path138.extname(entry.name).toLowerCase();
120254
+ const ext = path143.extname(entry.name).toLowerCase();
120092
120255
  const baseName = entry.name.toLowerCase();
120093
120256
  if (![".ts", ".js", ".mjs"].includes(ext)) {
120094
120257
  continue;
@@ -120106,7 +120269,7 @@ function extractRoutes(cwd) {
120106
120269
  }
120107
120270
  function extractRoutesFromFile(filePath) {
120108
120271
  const routes = [];
120109
- const content = fs100.readFileSync(filePath, "utf-8");
120272
+ const content = fs105.readFileSync(filePath, "utf-8");
120110
120273
  const lines = content.split(/\r?\n/);
120111
120274
  const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
120112
120275
  const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
@@ -120255,8 +120418,8 @@ init_zod();
120255
120418
  init_bun_compat();
120256
120419
  init_path_security();
120257
120420
  init_create_tool();
120258
- import * as fs101 from "node:fs";
120259
- import * as path139 from "node:path";
120421
+ import * as fs106 from "node:fs";
120422
+ import * as path144 from "node:path";
120260
120423
  var DEFAULT_MAX_RESULTS = 100;
120261
120424
  var DEFAULT_MAX_LINES = 200;
120262
120425
  var REGEX_TIMEOUT_MS = 5000;
@@ -120292,11 +120455,11 @@ function containsWindowsAttacks3(str) {
120292
120455
  }
120293
120456
  function isPathInWorkspace3(filePath, workspace) {
120294
120457
  try {
120295
- const resolvedPath = path139.resolve(workspace, filePath);
120296
- const realWorkspace = fs101.realpathSync(workspace);
120297
- const realResolvedPath = fs101.realpathSync(resolvedPath);
120298
- const relativePath = path139.relative(realWorkspace, realResolvedPath);
120299
- if (relativePath.startsWith("..") || path139.isAbsolute(relativePath)) {
120458
+ const resolvedPath = path144.resolve(workspace, filePath);
120459
+ const realWorkspace = fs106.realpathSync(workspace);
120460
+ const realResolvedPath = fs106.realpathSync(resolvedPath);
120461
+ const relativePath = path144.relative(realWorkspace, realResolvedPath);
120462
+ if (relativePath.startsWith("..") || path144.isAbsolute(relativePath)) {
120300
120463
  return false;
120301
120464
  }
120302
120465
  return true;
@@ -120309,12 +120472,12 @@ function validatePathForRead2(filePath, workspace) {
120309
120472
  }
120310
120473
  function findRgInEnvPath() {
120311
120474
  const searchPath = process.env.PATH ?? "";
120312
- for (const dir of searchPath.split(path139.delimiter)) {
120475
+ for (const dir of searchPath.split(path144.delimiter)) {
120313
120476
  if (!dir)
120314
120477
  continue;
120315
120478
  const isWindows = process.platform === "win32";
120316
- const candidate = path139.join(dir, isWindows ? "rg.exe" : "rg");
120317
- if (fs101.existsSync(candidate))
120479
+ const candidate = path144.join(dir, isWindows ? "rg.exe" : "rg");
120480
+ if (fs106.existsSync(candidate))
120318
120481
  return candidate;
120319
120482
  }
120320
120483
  return null;
@@ -120441,10 +120604,10 @@ function collectFiles(dir, workspace, includeGlobs, excludeGlobs) {
120441
120604
  return files;
120442
120605
  }
120443
120606
  try {
120444
- const entries = fs101.readdirSync(dir, { withFileTypes: true });
120607
+ const entries = fs106.readdirSync(dir, { withFileTypes: true });
120445
120608
  for (const entry of entries) {
120446
- const fullPath = path139.join(dir, entry.name);
120447
- const relativePath = path139.relative(workspace, fullPath);
120609
+ const fullPath = path144.join(dir, entry.name);
120610
+ const relativePath = path144.relative(workspace, fullPath);
120448
120611
  if (!validatePathForRead2(fullPath, workspace)) {
120449
120612
  continue;
120450
120613
  }
@@ -120485,13 +120648,13 @@ async function fallbackSearch(opts) {
120485
120648
  const matches = [];
120486
120649
  let total = 0;
120487
120650
  for (const file3 of files) {
120488
- const fullPath = path139.join(opts.workspace, file3);
120651
+ const fullPath = path144.join(opts.workspace, file3);
120489
120652
  if (!validatePathForRead2(fullPath, opts.workspace)) {
120490
120653
  continue;
120491
120654
  }
120492
120655
  let stats;
120493
120656
  try {
120494
- stats = fs101.statSync(fullPath);
120657
+ stats = fs106.statSync(fullPath);
120495
120658
  if (stats.size > MAX_FILE_SIZE_BYTES10) {
120496
120659
  continue;
120497
120660
  }
@@ -120500,7 +120663,7 @@ async function fallbackSearch(opts) {
120500
120663
  }
120501
120664
  let content;
120502
120665
  try {
120503
- content = fs101.readFileSync(fullPath, "utf-8");
120666
+ content = fs106.readFileSync(fullPath, "utf-8");
120504
120667
  } catch {
120505
120668
  continue;
120506
120669
  }
@@ -120612,7 +120775,7 @@ var search = createSwarmTool({
120612
120775
  message: "Exclude pattern contains invalid Windows-specific sequence"
120613
120776
  }, null, 2);
120614
120777
  }
120615
- if (!fs101.existsSync(directory)) {
120778
+ if (!fs106.existsSync(directory)) {
120616
120779
  return JSON.stringify({
120617
120780
  error: true,
120618
120781
  type: "unknown",
@@ -120890,7 +121053,7 @@ init_config();
120890
121053
  init_schema();
120891
121054
  init_create_tool();
120892
121055
  import { mkdir as mkdir23, rename as rename9, writeFile as writeFile18 } from "node:fs/promises";
120893
- import * as path140 from "node:path";
121056
+ import * as path145 from "node:path";
120894
121057
  var MAX_SPEC_BYTES = 256 * 1024;
120895
121058
  var spec_write = createSwarmTool({
120896
121059
  description: "Write the canonical project spec to .swarm/spec.md. Atomic write, size-bounded (256 KiB), heading-required. Honors spec_writer.allow_spec_write.",
@@ -120931,14 +121094,14 @@ var spec_write = createSwarmTool({
120931
121094
  reason: 'spec must contain at least one top-level "# Heading"'
120932
121095
  }, null, 2);
120933
121096
  }
120934
- const target = path140.join(directory, ".swarm", "spec.md");
120935
- await mkdir23(path140.dirname(target), { recursive: true });
121097
+ const target = path145.join(directory, ".swarm", "spec.md");
121098
+ await mkdir23(path145.dirname(target), { recursive: true });
120936
121099
  const tmp = `${target}.tmp-${process.pid}-${Date.now()}`;
120937
121100
  let finalContent = content;
120938
121101
  if (mode === "append") {
120939
121102
  try {
120940
- const fs102 = await import("node:fs/promises");
120941
- const prior = await fs102.readFile(target, "utf-8");
121103
+ const fs107 = await import("node:fs/promises");
121104
+ const prior = await fs107.readFile(target, "utf-8");
120942
121105
  finalContent = `${prior.replace(/\s+$/, "")}
120943
121106
 
120944
121107
  ${content}
@@ -120962,12 +121125,12 @@ init_loader();
120962
121125
  import {
120963
121126
  existsSync as existsSync82,
120964
121127
  mkdirSync as mkdirSync35,
120965
- readFileSync as readFileSync63,
121128
+ readFileSync as readFileSync68,
120966
121129
  renameSync as renameSync21,
120967
121130
  unlinkSync as unlinkSync18,
120968
121131
  writeFileSync as writeFileSync28
120969
121132
  } from "node:fs";
120970
- import path141 from "node:path";
121133
+ import path146 from "node:path";
120971
121134
  init_create_tool();
120972
121135
  init_resolve_working_directory();
120973
121136
  var VerdictSchema2 = exports_external.object({
@@ -121097,9 +121260,9 @@ var submit_phase_council_verdicts = createSwarmTool({
121097
121260
  }
121098
121261
  });
121099
121262
  function getPhaseMutationGapFinding(phaseNumber, workingDir) {
121100
- const mutationGatePath = path141.join(workingDir, ".swarm", "evidence", String(phaseNumber), "mutation-gate.json");
121263
+ const mutationGatePath = path146.join(workingDir, ".swarm", "evidence", String(phaseNumber), "mutation-gate.json");
121101
121264
  try {
121102
- const raw = readFileSync63(mutationGatePath, "utf-8");
121265
+ const raw = readFileSync68(mutationGatePath, "utf-8");
121103
121266
  const parsed = JSON.parse(raw);
121104
121267
  const gateEntry = (parsed.entries ?? []).find((entry) => entry?.type === "mutation-gate");
121105
121268
  if (!gateEntry) {
@@ -121159,9 +121322,9 @@ function getPhaseMutationGapFinding(phaseNumber, workingDir) {
121159
121322
  }
121160
121323
  }
121161
121324
  function writePhaseCouncilEvidence(workingDir, synthesis) {
121162
- const evidenceDir = path141.join(workingDir, ".swarm", "evidence", String(synthesis.phaseNumber));
121325
+ const evidenceDir = path146.join(workingDir, ".swarm", "evidence", String(synthesis.phaseNumber));
121163
121326
  mkdirSync35(evidenceDir, { recursive: true });
121164
- const evidenceFile = path141.join(evidenceDir, "phase-council.json");
121327
+ const evidenceFile = path146.join(evidenceDir, "phase-council.json");
121165
121328
  const evidenceBundle = {
121166
121329
  entries: [
121167
121330
  {
@@ -121530,7 +121693,7 @@ init_schema3();
121530
121693
  init_store();
121531
121694
  init_create_tool();
121532
121695
  init_resolve_working_directory();
121533
- import * as path142 from "node:path";
121696
+ import * as path147 from "node:path";
121534
121697
  var FindingSchema2 = exports_external.object({
121535
121698
  severity: exports_external.enum(["low", "medium", "high", "critical"]),
121536
121699
  category: exports_external.string().min(1),
@@ -121594,7 +121757,7 @@ var write_architecture_supervisor_evidence = createSwarmTool({
121594
121757
  if (config3.architectural_supervision?.persist_knowledge_recommendations && args2.knowledge_recommendations.length > 0) {
121595
121758
  const knowledgeConfig = KnowledgeConfigSchema.parse(config3.knowledge ?? {});
121596
121759
  const lessons = args2.knowledge_recommendations.map((r) => r.lesson);
121597
- const result = await curateAndStoreSwarm(lessons, path142.basename(dirResult.directory), { phase_number: args2.phase }, dirResult.directory, knowledgeConfig, { skipAutoPromotion: true });
121760
+ const result = await curateAndStoreSwarm(lessons, path147.basename(dirResult.directory), { phase_number: args2.phase }, dirResult.directory, knowledgeConfig, { skipAutoPromotion: true });
121598
121761
  knowledgeProposed = result.stored;
121599
121762
  }
121600
121763
  } catch {}
@@ -121625,8 +121788,8 @@ var write_architecture_supervisor_evidence = createSwarmTool({
121625
121788
  init_zod();
121626
121789
  init_path_security();
121627
121790
  init_create_tool();
121628
- import * as fs102 from "node:fs";
121629
- import * as path143 from "node:path";
121791
+ import * as fs107 from "node:fs";
121792
+ import * as path148 from "node:path";
121630
121793
  var WINDOWS_RESERVED_NAMES4 = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
121631
121794
  function containsWindowsAttacks4(str) {
121632
121795
  if (/:[^\\/]/.test(str))
@@ -121640,14 +121803,14 @@ function containsWindowsAttacks4(str) {
121640
121803
  }
121641
121804
  function isPathInWorkspace4(filePath, workspace) {
121642
121805
  try {
121643
- const resolvedPath = path143.resolve(workspace, filePath);
121644
- if (!fs102.existsSync(resolvedPath)) {
121806
+ const resolvedPath = path148.resolve(workspace, filePath);
121807
+ if (!fs107.existsSync(resolvedPath)) {
121645
121808
  return true;
121646
121809
  }
121647
- const realWorkspace = fs102.realpathSync(workspace);
121648
- const realResolvedPath = fs102.realpathSync(resolvedPath);
121649
- const relativePath = path143.relative(realWorkspace, realResolvedPath);
121650
- if (relativePath.startsWith("..") || path143.isAbsolute(relativePath)) {
121810
+ const realWorkspace = fs107.realpathSync(workspace);
121811
+ const realResolvedPath = fs107.realpathSync(resolvedPath);
121812
+ const relativePath = path148.relative(realWorkspace, realResolvedPath);
121813
+ if (relativePath.startsWith("..") || path148.isAbsolute(relativePath)) {
121651
121814
  return false;
121652
121815
  }
121653
121816
  return true;
@@ -121819,7 +121982,7 @@ var suggestPatch = createSwarmTool({
121819
121982
  message: "changes cannot be empty"
121820
121983
  }, null, 2);
121821
121984
  }
121822
- if (!fs102.existsSync(directory)) {
121985
+ if (!fs107.existsSync(directory)) {
121823
121986
  return JSON.stringify({
121824
121987
  success: false,
121825
121988
  error: true,
@@ -121855,8 +122018,8 @@ var suggestPatch = createSwarmTool({
121855
122018
  });
121856
122019
  continue;
121857
122020
  }
121858
- const fullPath = path143.resolve(directory, change.file);
121859
- if (!fs102.existsSync(fullPath)) {
122021
+ const fullPath = path148.resolve(directory, change.file);
122022
+ if (!fs107.existsSync(fullPath)) {
121860
122023
  errors5.push({
121861
122024
  success: false,
121862
122025
  error: true,
@@ -121870,7 +122033,7 @@ var suggestPatch = createSwarmTool({
121870
122033
  }
121871
122034
  let content;
121872
122035
  try {
121873
- content = fs102.readFileSync(fullPath, "utf-8");
122036
+ content = fs107.readFileSync(fullPath, "utf-8");
121874
122037
  } catch (err3) {
121875
122038
  errors5.push({
121876
122039
  success: false,
@@ -122158,12 +122321,12 @@ var lean_turbo_acquire_locks = createSwarmTool({
122158
122321
  // src/tools/lean-turbo-plan-lanes.ts
122159
122322
  init_zod();
122160
122323
  init_constants();
122161
- import * as fs104 from "node:fs";
122162
- import * as path145 from "node:path";
122324
+ import * as fs109 from "node:fs";
122325
+ import * as path150 from "node:path";
122163
122326
 
122164
122327
  // src/turbo/lean/conflicts.ts
122165
- import * as fs103 from "node:fs";
122166
- import * as path144 from "node:path";
122328
+ import * as fs108 from "node:fs";
122329
+ import * as path149 from "node:path";
122167
122330
  var DEFAULT_GLOBAL_FILES = [
122168
122331
  "package.json",
122169
122332
  "package-lock.json",
@@ -122290,12 +122453,12 @@ function isProtectedPath2(normalizedPath) {
122290
122453
  return false;
122291
122454
  }
122292
122455
  function readTaskScopes(directory, taskId) {
122293
- const scopePath = path144.join(directory, ".swarm", "scopes", `scope-${taskId}.json`);
122456
+ const scopePath = path149.join(directory, ".swarm", "scopes", `scope-${taskId}.json`);
122294
122457
  try {
122295
- if (!fs103.existsSync(scopePath)) {
122458
+ if (!fs108.existsSync(scopePath)) {
122296
122459
  return null;
122297
122460
  }
122298
- const raw = fs103.readFileSync(scopePath, "utf-8");
122461
+ const raw = fs108.readFileSync(scopePath, "utf-8");
122299
122462
  const parsed = JSON.parse(raw);
122300
122463
  if (!parsed || !Array.isArray(parsed.files)) {
122301
122464
  return null;
@@ -122678,12 +122841,12 @@ function createEmptyPlan(phaseNumber, planId) {
122678
122841
  // src/tools/lean-turbo-plan-lanes.ts
122679
122842
  init_create_tool();
122680
122843
  function readPlanJson(directory) {
122681
- const planPath = path145.join(directory, ".swarm", "plan.json");
122682
- if (!fs104.existsSync(planPath)) {
122844
+ const planPath = path150.join(directory, ".swarm", "plan.json");
122845
+ if (!fs109.existsSync(planPath)) {
122683
122846
  return null;
122684
122847
  }
122685
122848
  try {
122686
- return JSON.parse(fs104.readFileSync(planPath, "utf-8"));
122849
+ return JSON.parse(fs109.readFileSync(planPath, "utf-8"));
122687
122850
  } catch {
122688
122851
  return null;
122689
122852
  }
@@ -122732,8 +122895,8 @@ init_config();
122732
122895
 
122733
122896
  // src/turbo/lean/reviewer.ts
122734
122897
  init_state();
122735
- import * as fs105 from "node:fs/promises";
122736
- import * as path146 from "node:path";
122898
+ import * as fs110 from "node:fs/promises";
122899
+ import * as path151 from "node:path";
122737
122900
  init_state3();
122738
122901
  var DEFAULT_CONFIG3 = {
122739
122902
  reviewerAgent: "",
@@ -122849,9 +123012,9 @@ function parseReviewerVerdict(responseText) {
122849
123012
  return { verdict, reason };
122850
123013
  }
122851
123014
  async function writeReviewerEvidence(directory, phase, verdict, reason) {
122852
- const evidenceDir = path146.join(directory, ".swarm", "evidence", String(phase));
122853
- await fs105.mkdir(evidenceDir, { recursive: true });
122854
- const evidencePath = path146.join(evidenceDir, "lean-turbo-reviewer.json");
123015
+ const evidenceDir = path151.join(directory, ".swarm", "evidence", String(phase));
123016
+ await fs110.mkdir(evidenceDir, { recursive: true });
123017
+ const evidencePath = path151.join(evidenceDir, "lean-turbo-reviewer.json");
122855
123018
  const content = JSON.stringify({
122856
123019
  phase,
122857
123020
  verdict,
@@ -122860,11 +123023,11 @@ async function writeReviewerEvidence(directory, phase, verdict, reason) {
122860
123023
  }, null, 2);
122861
123024
  const tempPath = `${evidencePath}.tmp.${process.pid}.${Date.now()}`;
122862
123025
  try {
122863
- await fs105.writeFile(tempPath, content, "utf-8");
122864
- await fs105.rename(tempPath, evidencePath);
123026
+ await fs110.writeFile(tempPath, content, "utf-8");
123027
+ await fs110.rename(tempPath, evidencePath);
122865
123028
  } catch (error93) {
122866
123029
  try {
122867
- await fs105.unlink(tempPath);
123030
+ await fs110.unlink(tempPath);
122868
123031
  } catch {}
122869
123032
  throw error93;
122870
123033
  }
@@ -123655,8 +123818,8 @@ var lean_turbo_status = createSwarmTool({
123655
123818
  // src/tools/lint-spec.ts
123656
123819
  init_spec_schema();
123657
123820
  init_create_tool();
123658
- import * as fs106 from "node:fs";
123659
- import * as path147 from "node:path";
123821
+ import * as fs111 from "node:fs";
123822
+ import * as path152 from "node:path";
123660
123823
  var SPEC_FILE_NAME = "spec.md";
123661
123824
  var SWARM_DIR2 = ".swarm";
123662
123825
  var OBLIGATION_KEYWORDS2 = ["MUST", "SHALL", "SHOULD", "MAY"];
@@ -123709,8 +123872,8 @@ var lint_spec = createSwarmTool({
123709
123872
  async execute(_args, directory) {
123710
123873
  const errors5 = [];
123711
123874
  const warnings = [];
123712
- const specPath = path147.join(directory, SWARM_DIR2, SPEC_FILE_NAME);
123713
- if (!fs106.existsSync(specPath)) {
123875
+ const specPath = path152.join(directory, SWARM_DIR2, SPEC_FILE_NAME);
123876
+ if (!fs111.existsSync(specPath)) {
123714
123877
  const result2 = {
123715
123878
  valid: false,
123716
123879
  specMtime: null,
@@ -123729,12 +123892,12 @@ var lint_spec = createSwarmTool({
123729
123892
  }
123730
123893
  let specMtime = null;
123731
123894
  try {
123732
- const stats = fs106.statSync(specPath);
123895
+ const stats = fs111.statSync(specPath);
123733
123896
  specMtime = stats.mtime.toISOString();
123734
123897
  } catch {}
123735
123898
  let content;
123736
123899
  try {
123737
- content = fs106.readFileSync(specPath, "utf-8");
123900
+ content = fs111.readFileSync(specPath, "utf-8");
123738
123901
  } catch (e) {
123739
123902
  const result2 = {
123740
123903
  valid: false,
@@ -123779,13 +123942,13 @@ var lint_spec = createSwarmTool({
123779
123942
  });
123780
123943
  // src/tools/mutation-test.ts
123781
123944
  init_zod();
123782
- import * as fs107 from "node:fs";
123783
- import * as path149 from "node:path";
123945
+ import * as fs112 from "node:fs";
123946
+ import * as path154 from "node:path";
123784
123947
 
123785
123948
  // src/mutation/engine.ts
123786
123949
  import { spawnSync as spawnSync8 } from "node:child_process";
123787
123950
  import { unlinkSync as unlinkSync19, writeFileSync as writeFileSync29 } from "node:fs";
123788
- import * as path148 from "node:path";
123951
+ import * as path153 from "node:path";
123789
123952
 
123790
123953
  // src/mutation/equivalence.ts
123791
123954
  function isStaticallyEquivalent(originalCode, mutatedCode) {
@@ -123931,7 +124094,7 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
123931
124094
  let patchFile;
123932
124095
  try {
123933
124096
  const safeId2 = patch.id.replace(/[^a-zA-Z0-9_-]/g, "_");
123934
- patchFile = path148.join(workingDir, `.mutation_patch_${safeId2}.diff`);
124097
+ patchFile = path153.join(workingDir, `.mutation_patch_${safeId2}.diff`);
123935
124098
  try {
123936
124099
  writeFileSync29(patchFile, patch.patch);
123937
124100
  } catch (writeErr) {
@@ -124335,8 +124498,8 @@ var mutation_test = createSwarmTool({
124335
124498
  ];
124336
124499
  for (const filePath of uniquePaths) {
124337
124500
  try {
124338
- const resolvedPath = path149.resolve(cwd, filePath);
124339
- sourceFiles.set(filePath, fs107.readFileSync(resolvedPath, "utf-8"));
124501
+ const resolvedPath = path154.resolve(cwd, filePath);
124502
+ sourceFiles.set(filePath, fs112.readFileSync(resolvedPath, "utf-8"));
124340
124503
  } catch {}
124341
124504
  }
124342
124505
  const report = await executeMutationSuite(typedArgs.patches, typedArgs.test_command, typedArgs.files, cwd, undefined, undefined, sourceFiles.size > 0 ? sourceFiles : undefined);
@@ -124354,8 +124517,8 @@ var mutation_test = createSwarmTool({
124354
124517
  init_zod();
124355
124518
  init_manager2();
124356
124519
  init_detector();
124357
- import * as fs108 from "node:fs";
124358
- import * as path150 from "node:path";
124520
+ import * as fs113 from "node:fs";
124521
+ import * as path155 from "node:path";
124359
124522
  init_create_tool();
124360
124523
  var MAX_FILE_SIZE2 = 2 * 1024 * 1024;
124361
124524
  var BINARY_CHECK_BYTES = 8192;
@@ -124421,7 +124584,7 @@ async function syntaxCheck(input, directory, config3) {
124421
124584
  if (languages?.length) {
124422
124585
  const lowerLangs = languages.map((l) => l.toLowerCase());
124423
124586
  filesToCheck = filesToCheck.filter((file3) => {
124424
- const ext = path150.extname(file3.path).toLowerCase();
124587
+ const ext = path155.extname(file3.path).toLowerCase();
124425
124588
  const langDef = getLanguageForExtension(ext);
124426
124589
  const fileProfile = getProfileForFile(file3.path);
124427
124590
  const langId = fileProfile?.id || langDef?.id;
@@ -124434,7 +124597,7 @@ async function syntaxCheck(input, directory, config3) {
124434
124597
  let skippedCount = 0;
124435
124598
  for (const fileInfo of filesToCheck) {
124436
124599
  const { path: filePath } = fileInfo;
124437
- const fullPath = path150.isAbsolute(filePath) ? filePath : path150.join(directory, filePath);
124600
+ const fullPath = path155.isAbsolute(filePath) ? filePath : path155.join(directory, filePath);
124438
124601
  const result = {
124439
124602
  path: filePath,
124440
124603
  language: "",
@@ -124464,7 +124627,7 @@ async function syntaxCheck(input, directory, config3) {
124464
124627
  }
124465
124628
  let content;
124466
124629
  try {
124467
- content = fs108.readFileSync(fullPath, "utf8");
124630
+ content = fs113.readFileSync(fullPath, "utf8");
124468
124631
  } catch {
124469
124632
  result.skipped_reason = "file_read_error";
124470
124633
  skippedCount++;
@@ -124483,7 +124646,7 @@ async function syntaxCheck(input, directory, config3) {
124483
124646
  results.push(result);
124484
124647
  continue;
124485
124648
  }
124486
- const ext = path150.extname(filePath).toLowerCase();
124649
+ const ext = path155.extname(filePath).toLowerCase();
124487
124650
  const langDef = getLanguageForExtension(ext);
124488
124651
  result.language = profile?.id || langDef?.id || "unknown";
124489
124652
  const errors5 = extractSyntaxErrors(parser, content);
@@ -124580,8 +124743,8 @@ init_zod();
124580
124743
  init_utils();
124581
124744
  init_create_tool();
124582
124745
  init_path_security();
124583
- import * as fs109 from "node:fs";
124584
- import * as path151 from "node:path";
124746
+ import * as fs114 from "node:fs";
124747
+ import * as path156 from "node:path";
124585
124748
  var MAX_TEXT_LENGTH = 200;
124586
124749
  var MAX_FILE_SIZE_BYTES11 = 1024 * 1024;
124587
124750
  var SUPPORTED_EXTENSIONS4 = new Set([
@@ -124647,9 +124810,9 @@ function validatePathsInput(paths, cwd) {
124647
124810
  return { error: "paths contains path traversal", resolvedPath: null };
124648
124811
  }
124649
124812
  try {
124650
- const resolvedPath = path151.resolve(paths);
124651
- const normalizedCwd = path151.resolve(cwd);
124652
- const normalizedResolved = path151.resolve(resolvedPath);
124813
+ const resolvedPath = path156.resolve(paths);
124814
+ const normalizedCwd = path156.resolve(cwd);
124815
+ const normalizedResolved = path156.resolve(resolvedPath);
124653
124816
  if (!normalizedResolved.startsWith(normalizedCwd)) {
124654
124817
  return {
124655
124818
  error: "paths must be within the current working directory",
@@ -124665,13 +124828,13 @@ function validatePathsInput(paths, cwd) {
124665
124828
  }
124666
124829
  }
124667
124830
  function isSupportedExtension(filePath) {
124668
- const ext = path151.extname(filePath).toLowerCase();
124831
+ const ext = path156.extname(filePath).toLowerCase();
124669
124832
  return SUPPORTED_EXTENSIONS4.has(ext);
124670
124833
  }
124671
124834
  function findSourceFiles3(dir, files = []) {
124672
124835
  let entries;
124673
124836
  try {
124674
- entries = fs109.readdirSync(dir);
124837
+ entries = fs114.readdirSync(dir);
124675
124838
  } catch {
124676
124839
  return files;
124677
124840
  }
@@ -124680,10 +124843,10 @@ function findSourceFiles3(dir, files = []) {
124680
124843
  if (SKIP_DIRECTORIES5.has(entry)) {
124681
124844
  continue;
124682
124845
  }
124683
- const fullPath = path151.join(dir, entry);
124846
+ const fullPath = path156.join(dir, entry);
124684
124847
  let stat9;
124685
124848
  try {
124686
- stat9 = fs109.statSync(fullPath);
124849
+ stat9 = fs114.statSync(fullPath);
124687
124850
  } catch {
124688
124851
  continue;
124689
124852
  }
@@ -124776,7 +124939,7 @@ var todo_extract = createSwarmTool({
124776
124939
  return JSON.stringify(errorResult, null, 2);
124777
124940
  }
124778
124941
  const scanPath = resolvedPath;
124779
- if (!fs109.existsSync(scanPath)) {
124942
+ if (!fs114.existsSync(scanPath)) {
124780
124943
  const errorResult = {
124781
124944
  error: `path not found: ${pathsInput}`,
124782
124945
  total: 0,
@@ -124786,13 +124949,13 @@ var todo_extract = createSwarmTool({
124786
124949
  return JSON.stringify(errorResult, null, 2);
124787
124950
  }
124788
124951
  const filesToScan = [];
124789
- const stat9 = fs109.statSync(scanPath);
124952
+ const stat9 = fs114.statSync(scanPath);
124790
124953
  if (stat9.isFile()) {
124791
124954
  if (isSupportedExtension(scanPath)) {
124792
124955
  filesToScan.push(scanPath);
124793
124956
  } else {
124794
124957
  const errorResult = {
124795
- error: `unsupported file extension: ${path151.extname(scanPath)}`,
124958
+ error: `unsupported file extension: ${path156.extname(scanPath)}`,
124796
124959
  total: 0,
124797
124960
  byPriority: { high: 0, medium: 0, low: 0 },
124798
124961
  entries: []
@@ -124805,11 +124968,11 @@ var todo_extract = createSwarmTool({
124805
124968
  const allEntries = [];
124806
124969
  for (const filePath of filesToScan) {
124807
124970
  try {
124808
- const fileStat = fs109.statSync(filePath);
124971
+ const fileStat = fs114.statSync(filePath);
124809
124972
  if (fileStat.size > MAX_FILE_SIZE_BYTES11) {
124810
124973
  continue;
124811
124974
  }
124812
- const content = fs109.readFileSync(filePath, "utf-8");
124975
+ const content = fs114.readFileSync(filePath, "utf-8");
124813
124976
  const entries = parseTodoComments(content, filePath, tagsSet);
124814
124977
  allEntries.push(...entries);
124815
124978
  } catch {}
@@ -124840,18 +125003,18 @@ init_loader();
124840
125003
  init_schema();
124841
125004
  init_qa_gate_profile();
124842
125005
  init_gate_evidence();
124843
- import * as fs113 from "node:fs";
124844
- import * as path155 from "node:path";
125006
+ import * as fs118 from "node:fs";
125007
+ import * as path160 from "node:path";
124845
125008
 
124846
125009
  // src/hooks/diff-scope.ts
124847
125010
  init_bun_compat();
124848
- import * as fs111 from "node:fs";
124849
- import * as path153 from "node:path";
125011
+ import * as fs116 from "node:fs";
125012
+ import * as path158 from "node:path";
124850
125013
 
124851
125014
  // src/utils/gitignore-warning.ts
124852
125015
  init_bun_compat();
124853
- import * as fs110 from "node:fs";
124854
- import * as path152 from "node:path";
125016
+ import * as fs115 from "node:fs";
125017
+ import * as path157 from "node:path";
124855
125018
  var _internals67 = { bunSpawn };
124856
125019
  var _swarmGitExcludedChecked = false;
124857
125020
  function fileCoversSwarm(content) {
@@ -124925,16 +125088,16 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
124925
125088
  const excludeRelPath = excludePathRaw.trim();
124926
125089
  if (!excludeRelPath)
124927
125090
  return;
124928
- const excludePath = path152.isAbsolute(excludeRelPath) ? excludeRelPath : path152.join(directory, excludeRelPath);
125091
+ const excludePath = path157.isAbsolute(excludeRelPath) ? excludeRelPath : path157.join(directory, excludeRelPath);
124929
125092
  if (checkIgnoreExitCode !== 0) {
124930
125093
  try {
124931
- fs110.mkdirSync(path152.dirname(excludePath), { recursive: true });
125094
+ fs115.mkdirSync(path157.dirname(excludePath), { recursive: true });
124932
125095
  let existing = "";
124933
125096
  try {
124934
- existing = fs110.readFileSync(excludePath, "utf8");
125097
+ existing = fs115.readFileSync(excludePath, "utf8");
124935
125098
  } catch {}
124936
125099
  if (!fileCoversSwarm(existing)) {
124937
- fs110.appendFileSync(excludePath, `
125100
+ fs115.appendFileSync(excludePath, `
124938
125101
  # opencode-swarm local runtime state
124939
125102
  .swarm/
124940
125103
  `, "utf8");
@@ -124972,10 +125135,10 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
124972
125135
  var _internals68 = { bunSpawn };
124973
125136
  function getDeclaredScope(taskId, directory) {
124974
125137
  try {
124975
- const planPath = path153.join(directory, ".swarm", "plan.json");
124976
- if (!fs111.existsSync(planPath))
125138
+ const planPath = path158.join(directory, ".swarm", "plan.json");
125139
+ if (!fs116.existsSync(planPath))
124977
125140
  return null;
124978
- const raw = fs111.readFileSync(planPath, "utf-8");
125141
+ const raw = fs116.readFileSync(planPath, "utf-8");
124979
125142
  const plan = JSON.parse(raw);
124980
125143
  for (const phase of plan.phases ?? []) {
124981
125144
  for (const task of phase.tasks ?? []) {
@@ -125077,8 +125240,8 @@ init_telemetry();
125077
125240
 
125078
125241
  // src/turbo/lean/task-completion.ts
125079
125242
  init_file_locks();
125080
- import * as fs112 from "node:fs";
125081
- import * as path154 from "node:path";
125243
+ import * as fs117 from "node:fs";
125244
+ import * as path159 from "node:path";
125082
125245
  var _internals69 = {
125083
125246
  listActiveLocks,
125084
125247
  verifyLeanTurboTaskCompletion
@@ -125097,7 +125260,7 @@ var TIER_3_PATTERNS = [
125097
125260
  ];
125098
125261
  function matchesTier3Pattern(files) {
125099
125262
  for (const file3 of files) {
125100
- const fileName = path154.basename(file3);
125263
+ const fileName = path159.basename(file3);
125101
125264
  for (const pattern of TIER_3_PATTERNS) {
125102
125265
  if (pattern.test(fileName)) {
125103
125266
  return true;
@@ -125109,14 +125272,14 @@ function matchesTier3Pattern(files) {
125109
125272
  function verifyLeanTurboTaskCompletion(directory, taskId, sessionID) {
125110
125273
  let persisted = null;
125111
125274
  try {
125112
- const statePath = path154.join(directory, ".swarm", "turbo-state.json");
125113
- if (!fs112.existsSync(statePath)) {
125275
+ const statePath = path159.join(directory, ".swarm", "turbo-state.json");
125276
+ if (!fs117.existsSync(statePath)) {
125114
125277
  return {
125115
125278
  ok: false,
125116
125279
  reason: "Lean Turbo state file not found"
125117
125280
  };
125118
125281
  }
125119
- const raw = fs112.readFileSync(statePath, "utf-8");
125282
+ const raw = fs117.readFileSync(statePath, "utf-8");
125120
125283
  persisted = JSON.parse(raw);
125121
125284
  } catch {
125122
125285
  return {
@@ -125193,11 +125356,11 @@ function verifyLeanTurboTaskCompletion(directory, taskId, sessionID) {
125193
125356
  };
125194
125357
  }
125195
125358
  const phase = runState.phase ?? 0;
125196
- const evidencePath = path154.join(directory, ".swarm", "evidence", String(phase), "lean-turbo", `${lane.laneId}.json`);
125197
- const expectedDir = path154.join(directory, ".swarm", "evidence", String(phase), "lean-turbo");
125198
- const resolvedPath = path154.resolve(evidencePath);
125199
- const resolvedDir = path154.resolve(expectedDir);
125200
- if (!resolvedPath.startsWith(resolvedDir + path154.sep) && resolvedPath !== resolvedDir) {
125359
+ const evidencePath = path159.join(directory, ".swarm", "evidence", String(phase), "lean-turbo", `${lane.laneId}.json`);
125360
+ const expectedDir = path159.join(directory, ".swarm", "evidence", String(phase), "lean-turbo");
125361
+ const resolvedPath = path159.resolve(evidencePath);
125362
+ const resolvedDir = path159.resolve(expectedDir);
125363
+ if (!resolvedPath.startsWith(resolvedDir + path159.sep) && resolvedPath !== resolvedDir) {
125201
125364
  return {
125202
125365
  ok: false,
125203
125366
  reason: `Lane ID causes path traversal: ${lane.laneId}`,
@@ -125209,7 +125372,7 @@ function verifyLeanTurboTaskCompletion(directory, taskId, sessionID) {
125209
125372
  }
125210
125373
  };
125211
125374
  }
125212
- if (!fs112.existsSync(evidencePath)) {
125375
+ if (!fs117.existsSync(evidencePath)) {
125213
125376
  return {
125214
125377
  ok: false,
125215
125378
  reason: `Lane ${lane.laneId} evidence file not found: ${evidencePath}`,
@@ -125237,8 +125400,8 @@ function verifyLeanTurboTaskCompletion(directory, taskId, sessionID) {
125237
125400
  }
125238
125401
  let filesTouched = [];
125239
125402
  try {
125240
- const planPath = path154.join(directory, ".swarm", "plan.json");
125241
- const planRaw = fs112.readFileSync(planPath, "utf-8");
125403
+ const planPath = path159.join(directory, ".swarm", "plan.json");
125404
+ const planRaw = fs117.readFileSync(planPath, "utf-8");
125242
125405
  const plan = JSON.parse(planRaw);
125243
125406
  for (const planPhase of plan.phases ?? []) {
125244
125407
  for (const task of planPhase.tasks ?? []) {
@@ -125321,7 +125484,7 @@ var TIER_3_PATTERNS2 = [
125321
125484
  ];
125322
125485
  function matchesTier3Pattern2(files) {
125323
125486
  for (const file3 of files) {
125324
- const fileName = path155.basename(file3);
125487
+ const fileName = path160.basename(file3);
125325
125488
  for (const pattern of TIER_3_PATTERNS2) {
125326
125489
  if (pattern.test(fileName)) {
125327
125490
  return true;
@@ -125360,8 +125523,8 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
125360
125523
  if (!skipStandardTurboBypass && hasActiveTurboMode()) {
125361
125524
  const resolvedDir2 = workingDirectory;
125362
125525
  try {
125363
- const planPath = path155.join(resolvedDir2, ".swarm", "plan.json");
125364
- const planRaw = fs113.readFileSync(planPath, "utf-8");
125526
+ const planPath = path160.join(resolvedDir2, ".swarm", "plan.json");
125527
+ const planRaw = fs118.readFileSync(planPath, "utf-8");
125365
125528
  const plan = JSON.parse(planRaw);
125366
125529
  for (const planPhase of plan.phases ?? []) {
125367
125530
  for (const task of planPhase.tasks ?? []) {
@@ -125438,8 +125601,8 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
125438
125601
  }
125439
125602
  if (resolvedDir) {
125440
125603
  try {
125441
- const planPath = path155.join(resolvedDir, ".swarm", "plan.json");
125442
- const planRaw = fs113.readFileSync(planPath, "utf-8");
125604
+ const planPath = path160.join(resolvedDir, ".swarm", "plan.json");
125605
+ const planRaw = fs118.readFileSync(planPath, "utf-8");
125443
125606
  const plan = JSON.parse(planRaw);
125444
125607
  for (const planPhase of plan.phases ?? []) {
125445
125608
  for (const task of planPhase.tasks ?? []) {
@@ -125657,72 +125820,35 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
125657
125820
  session.currentTaskId = args2.task_id;
125658
125821
  }
125659
125822
  }
125660
- let normalizedDir;
125661
125823
  let directory;
125662
- if (args2.working_directory != null) {
125663
- if (args2.working_directory.includes("\x00")) {
125664
- return {
125665
- success: false,
125666
- message: "Invalid working_directory: null bytes are not allowed"
125667
- };
125668
- }
125669
- {
125670
- const devicePathPattern = /^\\\\|^(NUL|CON|AUX|COM[1-9]|LPT[1-9])(\..*)?$/i;
125671
- if (devicePathPattern.test(args2.working_directory)) {
125672
- return {
125673
- success: false,
125674
- message: "Invalid working_directory: Windows device paths are not allowed"
125675
- };
125676
- }
125677
- }
125678
- normalizedDir = path155.normalize(args2.working_directory);
125679
- const pathParts = normalizedDir.split(path155.sep);
125680
- if (pathParts.includes("..")) {
125681
- return {
125682
- success: false,
125683
- message: "Invalid working_directory: path traversal sequences (..) are not allowed",
125684
- errors: [
125685
- "Invalid working_directory: path traversal sequences (..) are not allowed"
125686
- ]
125687
- };
125688
- }
125689
- const resolvedDir = path155.resolve(normalizedDir);
125690
- try {
125691
- const realPath = fs113.realpathSync(resolvedDir);
125692
- const planPath = path155.join(realPath, ".swarm", "plan.json");
125693
- if (!fs113.existsSync(planPath)) {
125694
- return {
125695
- success: false,
125696
- message: `Invalid working_directory: plan not found in "${realPath}"`,
125697
- errors: [
125698
- `Invalid working_directory: plan not found in "${realPath}"`
125699
- ]
125700
- };
125701
- }
125702
- directory = realPath;
125703
- } catch {
125704
- return {
125705
- success: false,
125706
- message: `Invalid working_directory: path "${resolvedDir}" does not exist or is inaccessible`,
125707
- errors: [
125708
- `Invalid working_directory: path "${resolvedDir}" does not exist or is inaccessible`
125709
- ]
125710
- };
125711
- }
125712
- } else {
125713
- if (!fallbackDir) {
125714
- return {
125715
- success: false,
125716
- message: "No working_directory provided and fallbackDir is undefined",
125717
- errors: ["Cannot resolve directory for task status update"]
125718
- };
125719
- }
125720
- directory = fallbackDir;
125824
+ if (!args2.working_directory && !fallbackDir) {
125825
+ return {
125826
+ success: false,
125827
+ message: "No working_directory provided and fallbackDir is undefined",
125828
+ errors: ["Cannot resolve directory for task status update"]
125829
+ };
125830
+ }
125831
+ const resolveResult = resolveWorkingDirectory(args2.working_directory ?? fallbackDir, fallbackDir);
125832
+ if (!resolveResult.success) {
125833
+ return {
125834
+ success: false,
125835
+ message: resolveResult.message,
125836
+ errors: [resolveResult.message]
125837
+ };
125838
+ }
125839
+ directory = resolveResult.directory;
125840
+ const planPath = path160.join(directory, ".swarm", "plan.json");
125841
+ if (!fs118.existsSync(planPath)) {
125842
+ return {
125843
+ success: false,
125844
+ message: `Invalid working_directory: plan not found in "${directory}"`,
125845
+ errors: [`Invalid working_directory: plan not found in "${directory}"`]
125846
+ };
125721
125847
  }
125722
125848
  if (fallbackDir && directory !== fallbackDir) {
125723
- const canonicalDir = fs113.realpathSync(path155.resolve(directory));
125724
- const canonicalRoot = fs113.realpathSync(path155.resolve(fallbackDir));
125725
- if (canonicalDir.startsWith(canonicalRoot + path155.sep)) {
125849
+ const canonicalDir = fs118.realpathSync(path160.resolve(directory));
125850
+ const canonicalRoot = fs118.realpathSync(path160.resolve(fallbackDir));
125851
+ if (canonicalDir.startsWith(canonicalRoot + path160.sep)) {
125726
125852
  return {
125727
125853
  success: false,
125728
125854
  message: `Invalid working_directory: "${directory}" is a subdirectory of ` + `the project root "${fallbackDir}". Pass the project root path or ` + `omit working_directory entirely.`,
@@ -125734,22 +125860,22 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
125734
125860
  }
125735
125861
  if (args2.status === "in_progress") {
125736
125862
  try {
125737
- const evidencePath = path155.join(directory, ".swarm", "evidence", `${args2.task_id}.json`);
125738
- fs113.mkdirSync(path155.dirname(evidencePath), { recursive: true });
125739
- const fd = fs113.openSync(evidencePath, "wx");
125863
+ const evidencePath = path160.join(directory, ".swarm", "evidence", `${args2.task_id}.json`);
125864
+ fs118.mkdirSync(path160.dirname(evidencePath), { recursive: true });
125865
+ const fd = fs118.openSync(evidencePath, "wx");
125740
125866
  let writeOk = false;
125741
125867
  try {
125742
- fs113.writeSync(fd, JSON.stringify({
125868
+ fs118.writeSync(fd, JSON.stringify({
125743
125869
  taskId: args2.task_id,
125744
125870
  required_gates: [],
125745
125871
  gates: {}
125746
125872
  }, null, 2));
125747
125873
  writeOk = true;
125748
125874
  } finally {
125749
- fs113.closeSync(fd);
125875
+ fs118.closeSync(fd);
125750
125876
  if (!writeOk) {
125751
125877
  try {
125752
- fs113.unlinkSync(evidencePath);
125878
+ fs118.unlinkSync(evidencePath);
125753
125879
  } catch {}
125754
125880
  }
125755
125881
  }
@@ -125759,8 +125885,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
125759
125885
  recoverTaskStateFromDelegations(args2.task_id, directory);
125760
125886
  let phaseRequiresReviewer = true;
125761
125887
  try {
125762
- const planPath = path155.join(directory, ".swarm", "plan.json");
125763
- const planRaw = fs113.readFileSync(planPath, "utf-8");
125888
+ const planPath2 = path160.join(directory, ".swarm", "plan.json");
125889
+ const planRaw = fs118.readFileSync(planPath2, "utf-8");
125764
125890
  const plan = JSON.parse(planRaw);
125765
125891
  const taskPhase = plan.phases.find((p) => p.tasks.some((t) => t.id === args2.task_id));
125766
125892
  if (taskPhase?.required_agents && !taskPhase.required_agents.includes("reviewer")) {
@@ -125987,7 +126113,7 @@ init_utils2();
125987
126113
  init_redaction();
125988
126114
  import { createHash as createHash12 } from "node:crypto";
125989
126115
  import { appendFile as appendFile14, mkdir as mkdir25 } from "node:fs/promises";
125990
- import * as path156 from "node:path";
126116
+ import * as path161 from "node:path";
125991
126117
  var EVIDENCE_CACHE_FILE = "evidence-cache/documents.jsonl";
125992
126118
  var MAX_EVIDENCE_TEXT_LENGTH = 4000;
125993
126119
  async function writeEvidenceDocuments(directory, inputs, now = () => new Date) {
@@ -125995,7 +126121,7 @@ async function writeEvidenceDocuments(directory, inputs, now = () => new Date) {
125995
126121
  const capturedAt = now().toISOString();
125996
126122
  const records = inputs.map((input) => createEvidenceDocumentRecord(input, capturedAt)).filter((record3) => record3 !== null);
125997
126123
  if (records.length > 0) {
125998
- await mkdir25(path156.dirname(filePath), { recursive: true });
126124
+ await mkdir25(path161.dirname(filePath), { recursive: true });
125999
126125
  await appendFile14(filePath, `${records.map((record3) => JSON.stringify(record3)).join(`
126000
126126
  `)}
126001
126127
  `, "utf-8");
@@ -126188,8 +126314,8 @@ init_utils2();
126188
126314
  init_ledger();
126189
126315
  init_manager();
126190
126316
  init_create_tool();
126191
- import fs114 from "node:fs";
126192
- import path157 from "node:path";
126317
+ import fs119 from "node:fs";
126318
+ import path162 from "node:path";
126193
126319
  function normalizeVerdict(verdict) {
126194
126320
  switch (verdict) {
126195
126321
  case "APPROVED":
@@ -126237,7 +126363,7 @@ async function executeWriteDriftEvidence(args2, directory) {
126237
126363
  entries: [evidenceEntry]
126238
126364
  };
126239
126365
  const filename = "drift-verifier.json";
126240
- const relativePath = path157.join("evidence", String(phase), filename);
126366
+ const relativePath = path162.join("evidence", String(phase), filename);
126241
126367
  let validatedPath;
126242
126368
  try {
126243
126369
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -126248,12 +126374,12 @@ async function executeWriteDriftEvidence(args2, directory) {
126248
126374
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
126249
126375
  }, null, 2);
126250
126376
  }
126251
- const evidenceDir = path157.dirname(validatedPath);
126377
+ const evidenceDir = path162.dirname(validatedPath);
126252
126378
  try {
126253
- await fs114.promises.mkdir(evidenceDir, { recursive: true });
126254
- const tempPath = path157.join(evidenceDir, `.${filename}.tmp`);
126255
- await fs114.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
126256
- await fs114.promises.rename(tempPath, validatedPath);
126379
+ await fs119.promises.mkdir(evidenceDir, { recursive: true });
126380
+ const tempPath = path162.join(evidenceDir, `.${filename}.tmp`);
126381
+ await fs119.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
126382
+ await fs119.promises.rename(tempPath, validatedPath);
126257
126383
  let snapshotInfo;
126258
126384
  let snapshotError;
126259
126385
  let qaProfileLocked;
@@ -126346,8 +126472,8 @@ var write_drift_evidence = createSwarmTool({
126346
126472
  // src/tools/write-final-council-evidence.ts
126347
126473
  init_zod();
126348
126474
  init_loader();
126349
- import fs115 from "node:fs";
126350
- import path158 from "node:path";
126475
+ import fs120 from "node:fs";
126476
+ import path163 from "node:path";
126351
126477
  init_utils2();
126352
126478
  init_manager();
126353
126479
  init_create_tool();
@@ -126435,7 +126561,7 @@ async function executeWriteFinalCouncilEvidence(args2, directory) {
126435
126561
  timestamp: synthesis.timestamp
126436
126562
  };
126437
126563
  const filename = "final-council.json";
126438
- const relativePath = path158.join("evidence", filename);
126564
+ const relativePath = path163.join("evidence", filename);
126439
126565
  let validatedPath;
126440
126566
  try {
126441
126567
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -126449,12 +126575,12 @@ async function executeWriteFinalCouncilEvidence(args2, directory) {
126449
126575
  const evidenceContent = {
126450
126576
  entries: [evidenceEntry]
126451
126577
  };
126452
- const evidenceDir = path158.dirname(validatedPath);
126578
+ const evidenceDir = path163.dirname(validatedPath);
126453
126579
  try {
126454
- await fs115.promises.mkdir(evidenceDir, { recursive: true });
126455
- const tempPath = path158.join(evidenceDir, `.${filename}.tmp`);
126456
- await fs115.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
126457
- await fs115.promises.rename(tempPath, validatedPath);
126580
+ await fs120.promises.mkdir(evidenceDir, { recursive: true });
126581
+ const tempPath = path163.join(evidenceDir, `.${filename}.tmp`);
126582
+ await fs120.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
126583
+ await fs120.promises.rename(tempPath, validatedPath);
126458
126584
  return JSON.stringify({
126459
126585
  success: true,
126460
126586
  phase: input.phase,
@@ -126510,8 +126636,8 @@ var write_final_council_evidence = createSwarmTool({
126510
126636
  init_zod();
126511
126637
  init_utils2();
126512
126638
  init_create_tool();
126513
- import fs116 from "node:fs";
126514
- import path159 from "node:path";
126639
+ import fs121 from "node:fs";
126640
+ import path164 from "node:path";
126515
126641
  function normalizeVerdict2(verdict) {
126516
126642
  switch (verdict) {
126517
126643
  case "APPROVED":
@@ -126559,7 +126685,7 @@ async function executeWriteHallucinationEvidence(args2, directory) {
126559
126685
  entries: [evidenceEntry]
126560
126686
  };
126561
126687
  const filename = "hallucination-guard.json";
126562
- const relativePath = path159.join("evidence", String(phase), filename);
126688
+ const relativePath = path164.join("evidence", String(phase), filename);
126563
126689
  let validatedPath;
126564
126690
  try {
126565
126691
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -126570,12 +126696,12 @@ async function executeWriteHallucinationEvidence(args2, directory) {
126570
126696
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
126571
126697
  }, null, 2);
126572
126698
  }
126573
- const evidenceDir = path159.dirname(validatedPath);
126699
+ const evidenceDir = path164.dirname(validatedPath);
126574
126700
  try {
126575
- await fs116.promises.mkdir(evidenceDir, { recursive: true });
126576
- const tempPath = path159.join(evidenceDir, `.${filename}.tmp`);
126577
- await fs116.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
126578
- await fs116.promises.rename(tempPath, validatedPath);
126701
+ await fs121.promises.mkdir(evidenceDir, { recursive: true });
126702
+ const tempPath = path164.join(evidenceDir, `.${filename}.tmp`);
126703
+ await fs121.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
126704
+ await fs121.promises.rename(tempPath, validatedPath);
126579
126705
  return JSON.stringify({
126580
126706
  success: true,
126581
126707
  phase,
@@ -126621,8 +126747,8 @@ var write_hallucination_evidence = createSwarmTool({
126621
126747
  init_zod();
126622
126748
  init_utils2();
126623
126749
  init_create_tool();
126624
- import fs117 from "node:fs";
126625
- import path160 from "node:path";
126750
+ import fs122 from "node:fs";
126751
+ import path165 from "node:path";
126626
126752
  function normalizeVerdict3(verdict) {
126627
126753
  switch (verdict) {
126628
126754
  case "PASS":
@@ -126696,7 +126822,7 @@ async function executeWriteMutationEvidence(args2, directory) {
126696
126822
  entries: [evidenceEntry]
126697
126823
  };
126698
126824
  const filename = "mutation-gate.json";
126699
- const relativePath = path160.join("evidence", String(phase), filename);
126825
+ const relativePath = path165.join("evidence", String(phase), filename);
126700
126826
  let validatedPath;
126701
126827
  try {
126702
126828
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -126707,12 +126833,12 @@ async function executeWriteMutationEvidence(args2, directory) {
126707
126833
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
126708
126834
  }, null, 2);
126709
126835
  }
126710
- const evidenceDir = path160.dirname(validatedPath);
126836
+ const evidenceDir = path165.dirname(validatedPath);
126711
126837
  try {
126712
- await fs117.promises.mkdir(evidenceDir, { recursive: true });
126713
- const tempPath = path160.join(evidenceDir, `.${filename}.tmp`);
126714
- await fs117.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
126715
- await fs117.promises.rename(tempPath, validatedPath);
126838
+ await fs122.promises.mkdir(evidenceDir, { recursive: true });
126839
+ const tempPath = path165.join(evidenceDir, `.${filename}.tmp`);
126840
+ await fs122.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
126841
+ await fs122.promises.rename(tempPath, validatedPath);
126716
126842
  return JSON.stringify({
126717
126843
  success: true,
126718
126844
  phase,
@@ -127060,7 +127186,7 @@ async function initializeOpenCodeSwarm(ctx) {
127060
127186
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
127061
127187
  preflightTriggerManager = new PTM(automationConfig);
127062
127188
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
127063
- const swarmDir = path162.resolve(ctx.directory, ".swarm");
127189
+ const swarmDir = path167.resolve(ctx.directory, ".swarm");
127064
127190
  statusArtifact = new ASA(swarmDir);
127065
127191
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
127066
127192
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {
@@ -127657,7 +127783,7 @@ ${promptRaw}`;
127657
127783
  const meta3 = readSkillMetadata(s.skillPath, ctx.directory);
127658
127784
  let desc = meta3.description || "";
127659
127785
  if (!desc || desc === "No description provided") {
127660
- desc = path162.basename(path162.dirname(s.skillPath));
127786
+ desc = path167.basename(path167.dirname(s.skillPath));
127661
127787
  }
127662
127788
  desc = desc.replace(/,/g, ";");
127663
127789
  return `file:${s.skillPath} (-- ${desc})`;
@@ -127667,7 +127793,7 @@ ${promptRaw}`;
127667
127793
 
127668
127794
  ${promptRaw}`;
127669
127795
  argsRecord.prompt = newPrompt;
127670
- const skillNames = topSkills.map((s) => `${path162.basename(s.skillPath)} (score: ${s.score.toFixed(2)})`).join(", ");
127796
+ const skillNames = topSkills.map((s) => `${path167.basename(s.skillPath)} (score: ${s.score.toFixed(2)})`).join(", ");
127671
127797
  console.warn(`[skill-propagation-gate] Injected skills: ${skillNames}`);
127672
127798
  for (const skill of topSkills) {
127673
127799
  try {