specmem-hardwicksoftware 3.7.6 → 3.7.7

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.
@@ -4014,9 +4014,23 @@ def main():
4014
4014
 
4015
4015
  # Signal handling for graceful shutdown
4016
4016
  import signal
4017
+ import traceback
4017
4018
  def handle_signal(signum, frame):
4018
4019
  sig_name = signal.Signals(signum).name
4020
+ # DEBUG: Log who sent the signal for troubleshooting
4021
+ my_pid = os.getpid()
4022
+ my_ppid = os.getppid()
4019
4023
  print(f"\n⚡ Received {sig_name} - shutting down gracefully...", file=sys.stderr)
4024
+ print(f" DEBUG: my_pid={my_pid}, my_ppid={my_ppid}", file=sys.stderr)
4025
+ # Try to identify caller via /proc
4026
+ try:
4027
+ with open(f'/proc/{my_ppid}/cmdline', 'r') as f:
4028
+ parent_cmd = f.read().replace('\x00', ' ').strip()
4029
+ print(f" DEBUG: parent_cmd={parent_cmd[:200]}", file=sys.stderr)
4030
+ except Exception as e:
4031
+ print(f" DEBUG: could not read parent cmdline: {e}", file=sys.stderr)
4032
+ print(f" DEBUG: stack trace:", file=sys.stderr)
4033
+ traceback.print_stack(frame, file=sys.stderr)
4020
4034
  server.shutdown_requested = True
4021
4035
  # Stop QQMS v2 drain thread if enabled
4022
4036
  if qqms_v2_instance:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specmem-hardwicksoftware",
3
- "version": "3.7.6",
3
+ "version": "3.7.7",
4
4
  "type": "module",
5
5
  "description": "Persistent memory system for coding sessions - semantic search with pgvector, token compression, team coordination, file watching. Needs root: installs system-wide hooks, manages docker/PostgreSQL, writes global configs, handles screen sessions. justcalljon.pro",
6
6
  "main": "dist/index.js",
@@ -3659,15 +3659,18 @@ async function indexCodebase(projectPath, ui, embeddingResult) {
3659
3659
  try { fs.unlinkSync(projectSocketPath); } catch { /* ignore */ }
3660
3660
  }
3661
3661
  const pythonPath = getPythonPath();
3662
+ // CRITICAL: Use file descriptor stdio, NOT pipes. Piped stdio causes SIGTERM
3663
+ // when parent event loop is under heavy load during batch indexing.
3664
+ const revalLogPath = path.join(projectPath, 'specmem', 'sockets', 'embedding-autostart.log');
3665
+ const revalLogFd = fs.openSync(revalLogPath, 'a');
3662
3666
  const proc = spawn(pythonPath, [embeddingScript], {
3663
3667
  cwd: path.dirname(embeddingScript),
3664
3668
  env: { ...process.env, SPECMEM_SOCKET_PATH: projectSocketPath, SPECMEM_PROJECT_PATH: projectPath },
3665
3669
  detached: true,
3666
- stdio: ['ignore', 'pipe', 'pipe']
3670
+ stdio: ['ignore', revalLogFd, revalLogFd]
3667
3671
  });
3668
- proc.stdout.on('data', () => {});
3669
- proc.stderr.on('data', () => {});
3670
3672
  proc.on('error', () => {});
3673
+ fs.closeSync(revalLogFd);
3671
3674
  proc.unref();
3672
3675
  // Wait up to 15s for socket to appear
3673
3676
  for (let i = 0; i < 30; i++) {
@@ -4867,6 +4870,10 @@ async function extractSessions(projectPath, ui, embeddingResult = null) {
4867
4870
 
4868
4871
  // Task #22 fix: Use getPythonPath() instead of hardcoded 'python3'
4869
4872
  const pythonPath = getPythonPath();
4873
+ // CRITICAL: Use file descriptor stdio, NOT pipes. Piped stdio causes SIGTERM
4874
+ // when parent event loop is under heavy load during batch indexing.
4875
+ const sessEmbedLogPath = path.join(projectPath, 'specmem', 'sockets', 'embedding-autostart.log');
4876
+ const sessEmbedLogFd = fs.openSync(sessEmbedLogPath, 'a');
4870
4877
  const embeddingProcess = spawn(pythonPath, [embeddingScript], {
4871
4878
  cwd: path.dirname(embeddingScript),
4872
4879
  env: {
@@ -4875,7 +4882,7 @@ async function extractSessions(projectPath, ui, embeddingResult = null) {
4875
4882
  SPECMEM_PROJECT_PATH: projectPath
4876
4883
  },
4877
4884
  detached: true,
4878
- stdio: ['ignore', 'pipe', 'pipe']
4885
+ stdio: ['ignore', sessEmbedLogFd, sessEmbedLogFd]
4879
4886
  });
4880
4887
 
4881
4888
  // error handler BEFORE unref - prevents silent spawn failures
@@ -4883,14 +4890,7 @@ async function extractSessions(projectPath, ui, embeddingResult = null) {
4883
4890
  ui.setSubStatus('Embedding spawn error: ' + err.message);
4884
4891
  });
4885
4892
 
4886
- // CRITICAL: Consume stdout/stderr so Python startup banner doesn't leak to terminal
4887
- embeddingProcess.stdout.on('data', (chunk) => {
4888
- initLog('[EMBED-STDOUT] ' + chunk.toString().trim());
4889
- });
4890
- embeddingProcess.stderr.on('data', (chunk) => {
4891
- initLog('[EMBED-STDERR] ' + chunk.toString().trim());
4892
- });
4893
-
4893
+ fs.closeSync(sessEmbedLogFd);
4894
4894
  embeddingProcess.unref();
4895
4895
 
4896
4896
  // Wait for socket to appear (up to 30s)