@yemi33/minions 0.1.1861 → 0.1.1862

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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1862 (2026-05-11)
4
+
5
+ ### Fixes
6
+ - use 'exit' event for OS exit code + dedup sentinel; warn agents off .cmd shims
7
+
3
8
  ## 0.1.1861 (2026-05-10)
4
9
 
5
10
  ### Fixes
@@ -409,11 +409,38 @@ function main() {
409
409
  }, MCP_STARTUP_TIMEOUT);
410
410
  proc.stdout.once('data', () => { gotFirstOutput = true; clearTimeout(startupTimer); });
411
411
 
412
+ // Track the real OS exit code via the 'exit' event. Node's 'close' event
413
+ // can report code=0 on Windows when the OS-level exit was non-zero
414
+ // (observed empirically with both Claude Code CLI and Copilot CLI exiting
415
+ // with OS exit code 1 silently during long PowerShell tool calls — procdump
416
+ // captured the 1, but the engine's onAgentClose saw the spawn-agent's parent
417
+ // pipe report code=0). The 'exit' event fires earlier and carries the OS code
418
+ // more reliably; 'close' waits for stdio teardown which can race.
419
+ let realExitFromEvent = null;
420
+ let realSignalFromEvent = null;
421
+ let sentinelWritten = false;
422
+ proc.on('exit', (code, signal) => {
423
+ if (Number.isInteger(code)) realExitFromEvent = code;
424
+ if (signal) realSignalFromEvent = signal;
425
+ });
412
426
  proc.on('close', (code, signal) => {
413
427
  clearTimeout(startupTimer);
414
- const exitCode = normalizeRuntimeExit(code, signal);
415
- const sentinelResult = writeProcessExitSentinel({ exitCode, signal });
416
- fs.appendFileSync(debugPath, `EXIT: code=${exitCode}${signal ? ` signal=${signal}` : ''}\nSTDERR: ${stderrBuf.slice(0, 500)}\n`);
428
+ // Prefer the 'exit' event's code/signal when present — see note above.
429
+ const effectiveCode = (realExitFromEvent != null) ? realExitFromEvent : code;
430
+ const effectiveSignal = realSignalFromEvent || signal;
431
+ const exitCode = normalizeRuntimeExit(effectiveCode, effectiveSignal);
432
+ if (sentinelWritten) {
433
+ // Defense-in-depth: never write a duplicate sentinel. We observed pairs
434
+ // of [process-exit] code=0 lines in live-output.log across many failed
435
+ // runs, which suggests close has fired twice in some edge cases (e.g.,
436
+ // shim re-launch on Windows). One sentinel per spawn is the contract.
437
+ fs.appendFileSync(debugPath, `EXIT (duplicate close, skipping sentinel): code=${exitCode}${effectiveSignal ? ` signal=${effectiveSignal}` : ''}\n`);
438
+ process.exit(exitCode);
439
+ return;
440
+ }
441
+ sentinelWritten = true;
442
+ const sentinelResult = writeProcessExitSentinel({ exitCode, signal: effectiveSignal });
443
+ fs.appendFileSync(debugPath, `EXIT: code=${exitCode}${effectiveSignal ? ` signal=${effectiveSignal}` : ''} (close=${code} exit=${realExitFromEvent})\nSTDERR: ${stderrBuf.slice(0, 500)}\n`);
417
444
  if (!sentinelResult.fileWritten) {
418
445
  fs.appendFileSync(debugPath, `EXIT SENTINEL: file write failed for ${process.env.MINIONS_LIVE_OUTPUT_PATH}\n`);
419
446
  }
@@ -421,7 +448,10 @@ function main() {
421
448
  });
422
449
  proc.on('error', (err) => {
423
450
  fs.appendFileSync(debugPath, `ERROR: ${err.message}\n`);
424
- writeProcessExitSentinel({ exitCode: 1 });
451
+ if (!sentinelWritten) {
452
+ sentinelWritten = true;
453
+ writeProcessExitSentinel({ exitCode: 1 });
454
+ }
425
455
  process.exit(1);
426
456
  });
427
457
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1861",
3
+ "version": "0.1.1862",
4
4
  "description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
5
5
  "bin": {
6
6
  "minions": "bin/minions.js"
@@ -74,6 +74,37 @@ Use `status: "failed"` plus an accurate `failure_class`, `retryable`, and `needs
74
74
 
75
75
  **No-op completions:** when you correctly decline to do the work — the change was already shipped on master, the dispatch premise is wrong, the flagged review comment is your own author-notes, etc. — write `status: "success"`, `pr: "N/A"`, AND add `"noop": true`. The engine treats `noop: true` as the canonical signal that no PR was expected, marks the work item done with the rationale surfaced in `_noopReason` for the dashboard, and skips the missing-PR-attachment failure. Without `noop: true`, an empty PR will still be flagged as a silent failure and auto-retried up to `maxRetries` times.
76
76
 
77
+ ## Test Invocation — direct binary, not `.cmd` shims
78
+
79
+ When running tests via the shell/PowerShell tool, invoke the underlying
80
+ runner binary **directly** instead of going through a package-manager
81
+ wrapper. The runtime CLIs (Copilot CLI, Claude Code CLI) deterministically
82
+ crash on Windows when invoking `.cmd` shims (`npm`, `yarn`, `pnpm`, `npx`,
83
+ `mocha`, `jest`, `vitest`) for substantial test commands inside a real
84
+ agent-prompt session (confirmed 2026-05-11 by controlled reproduction).
85
+ The crash signature: silent exit with OS code 1, no result event, no error
86
+ message — the engine surfaces it as "phantom completion."
87
+
88
+ For test verification, prefer:
89
+
90
+ - ❌ `npm test`, `yarn test`, `pnpm test`, `npx vitest`, `npx jest`, `npx mocha`
91
+ - ✅ `node test/<file>.test.js` (Node projects with custom runners)
92
+ - ✅ `node node_modules/<runner>/bin/<runner>.js <args>` (resolve the runner manually)
93
+ - ✅ `python -m pytest tests/test_foo.py` (Python — `.exe`, not `.cmd`)
94
+ - ✅ `cargo test --test foo` (Rust)
95
+ - ✅ `dotnet test path/to/Project.Tests.csproj` (.NET)
96
+ - ✅ `go test ./...` (Go)
97
+
98
+ To map a package-manager wrapper to its direct invocation, check the
99
+ project's `package.json` `scripts` section (Node), `pyproject.toml` (Python),
100
+ `Cargo.toml` (Rust), or the project's CLAUDE.md / README. Direct invocations
101
+ are also significantly faster (no shim overhead).
102
+
103
+ If the project's test setup *requires* the wrapper (e.g., it sets env vars
104
+ or installs dependencies in a hook), report this in your completion block
105
+ and use targeted node invocations for verification anyway — the wrapper
106
+ crashes the agent before the test results land.
107
+
77
108
  ## Long-Running Commands
78
109
 
79
110
  Builds, dependency installs, tests, and local servers can be quiet for long periods. Run the repo's normal CLI commands and let them finish; do not add artificial progress output, heartbeat loops, or command-specific workarounds just to keep Minions active.