@volter/supercode-health 0.1.15 → 0.1.16

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/lib/features.mjs CHANGED
@@ -14,6 +14,7 @@ export const FEATURES = [
14
14
  { id: 'tcp', barrier: 'none', platforms: ['darwin', 'linux', 'win32'], reads: 'sockets per state, ephemeral ports in use, TIME_WAIT by listener and its pid' },
15
15
  { id: 'files', barrier: 'none', platforms: ['darwin', 'linux'], reads: 'open files against the system limit' },
16
16
  { id: 'recorded', barrier: 'none', platforms: ['darwin', 'linux'], reads: 'every command supercode recorded outside itself (harness configs, hooks baked into live sessions), one of each program and verb run as its consumer runs it' },
17
+ { id: 'markers', barrier: 'none', platforms: ['darwin', 'linux'], reads: "terminal windows and tmux processes carrying a harness's child-session mark; Claude Code on a terminal with no file in its session registry (one ps)" },
17
18
  { id: 'drift', barrier: 'none', platforms: ['darwin', 'linux'], reads: 'running supercode processes whose install was replaced after they started (one ps)' },
18
19
  { id: 'services', barrier: 'none', platforms: ['darwin', 'linux'], reads: "configured launchd/systemd user jobs: running, last exit, log silence" },
19
20
  { id: 'supercode', barrier: 'integration:machine-daemon', platforms: ['darwin', 'linux', 'win32'], reads: "this machine's supercode connector: answering, server link, stuck launches" },
@@ -0,0 +1,59 @@
1
+ // Harness marks where no session is (R75): a coding harness marks what its tools run (Claude Code's CLAUDECODE and
2
+ // CLAUDE_CODE_CHILD_SESSION), and a terminal window or tmux process that carries the marks hands them to every shell a
3
+ // person opens there. Claude Code started in such a shell takes itself for a nested session: it saves no transcript,
4
+ // writes no file in its session registry, and supercode's mail cannot tell who it is. Twelve windows on
5
+ // yuerans-macbook-pro carried the marks from 2026-10-07 to 10-09 and nothing read them. Two readings, one ps:
6
+ // - every terminal app and tmux process whose own environment holds a child-session mark with a value;
7
+ // - every Claude Code process on a terminal with no file in its session registry (`<claude home>/sessions/<pid>.json`).
8
+ // Only names and pids are kept: an environment's values never leave this function.
9
+ import { existsSync, readFileSync } from 'node:fs';
10
+ import { createRequire } from 'node:module';
11
+ import { homedir } from 'node:os';
12
+ import { join } from 'node:path';
13
+ import { run, clockSeconds } from './run.mjs';
14
+
15
+ /** The marks a harness takes itself for a nested session by: the terminal SDK's list, the one home of it. */
16
+ export function childSessionMarkers() {
17
+ const file = createRequire(import.meta.url).resolve('@volter/supercode-terminal/harness-markers.json');
18
+ return JSON.parse(readFileSync(file, 'utf8')).child_session;
19
+ }
20
+
21
+ const TERMINALS = new Set(['wezterm-gui', 'wezterm', 'kitty', 'ghostty', 'alacritty', 'iTerm2', 'Terminal', 'xterm', 'tmux']);
22
+ const CLAUDE = new Set(['claude', 'claude.exe']);
23
+ const program = (command) => (command.split(/\s+/u)[0] ?? '').split('/').pop();
24
+
25
+ /** One process's environment as [name, value] pairs: Linux's own file for it; on macOS `ps -E` printed it after the command. */
26
+ function environmentOf(pid, printed) {
27
+ if (process.platform === 'linux') {
28
+ try { return readFileSync(`/proc/${pid}/environ`, 'utf8').split('\0').map((entry) => entry.split(/=(.*)/su)); } catch { return []; }
29
+ }
30
+ return [...printed.matchAll(/(?:^|\s)([A-Za-z_][A-Za-z0-9_]*)=(\S*)/gu)].map((match) => [match[1], match[2]]);
31
+ }
32
+
33
+ /**
34
+ * { marked: [{ pid, program, markers }], unregistered: [{ pid, tty, ageSec, registry }] }. `minAgeSec` leaves a Claude
35
+ * Code process time to write its registry file.
36
+ */
37
+ export async function harnessMarks({ markers = childSessionMarkers(), minAgeSec = 120, ps = null } = {}) {
38
+ const listed = ps ?? await run('/bin/ps', [process.platform === 'linux' ? '-axww' : '-axEww', '-o', 'pid=,tty=,etime=,command='], { timeoutMs: 10_000 });
39
+ if (!listed.ok) throw new Error(`ps: ${listed.error}`);
40
+ const marked = [], unregistered = [];
41
+ for (const line of listed.stdout.split('\n')) {
42
+ const row = /^\s*(\d+)\s+(\S+)\s+(\S+)\s+(.*)$/u.exec(line);
43
+ if (!row) continue;
44
+ const [, pid, tty, etime, command] = row, name = program(command);
45
+ if (!TERMINALS.has(name) && !CLAUDE.has(name)) continue;
46
+ const environment = new Map(environmentOf(pid, command));
47
+ if (TERMINALS.has(name)) {
48
+ const carried = markers.filter((marker) => environment.get(marker));
49
+ if (carried.length) marked.push({ pid: Number(pid), program: name, markers: carried });
50
+ continue;
51
+ }
52
+ // a Claude Code process with no terminal is a tool's own child (a subagent, a print run): nobody resumes or mails it
53
+ if (/^\?+$|^-$/u.test(tty) || clockSeconds(etime) < minAgeSec) continue;
54
+ const home = environment.get('CLAUDE_CONFIG_DIR') || join(environment.get('HOME') || homedir(), '.claude');
55
+ const registry = join(home, 'sessions', `${pid}.json`);
56
+ if (!existsSync(registry)) unregistered.push({ pid: Number(pid), tty, ageSec: clockSeconds(etime), registry });
57
+ }
58
+ return { marked, unregistered };
59
+ }
package/lib/sample.mjs CHANGED
@@ -9,6 +9,7 @@ import { expiredFailures, failureSummary, keyFor, Ledger, runningRelease, sayOnC
9
9
  import { healthDir } from './paths.mjs';
10
10
  import { pinnedHomes, staleSupercode } from './drift.mjs';
11
11
  import { brokenRecorded } from './recorded.mjs';
12
+ import { harnessMarks } from './markers.mjs';
12
13
  import { join } from 'node:path';
13
14
 
14
15
  const MB = 1024 * 1024;
@@ -408,6 +409,17 @@ export class Sampler {
408
409
  slow.readings.recorded = broken;
409
410
  for (const b of broken) slow.observations.push({ key: `recorded.broken:${b.shape}`, family: 'recorded', def: alarm('recorded.broken'), value: b.count, met: { warn: true, crit: true, clears: false }, summary: `${b.count} recorded command(s) cannot run (${b.where.slice(0, 3).join(', ')}): ${b.error}; ${b.command.slice(0, 120)}`, detail: b });
410
411
  });
412
+ if (on.has('markers')) await attempt('markers', async () => {
413
+ const def = alarm('harness.unregistered');
414
+ const { marked, unregistered } = await harnessMarks({ minAgeSec: def.minAgeSec });
415
+ slow.families.add('marked'); slow.families.add('unregistered');
416
+ // every marked window and unregistered session this pass found; one no longer found is cleared
417
+ slow.observations.push({ enumerated: 'marked', family: 'marked', by: 'suffix', entities: new Set(marked.map((m) => String(m.pid))) });
418
+ slow.observations.push({ enumerated: 'unregistered', family: 'unregistered', by: 'suffix', entities: new Set(unregistered.map((u) => String(u.pid))) });
419
+ slow.readings.markers = { marked, unregistered };
420
+ for (const m of marked) slow.observations.push({ key: `harness.marked:${m.pid}`, family: 'marked', def: alarm('harness.marked'), value: 1, met: { warn: true, crit: true, clears: false }, summary: `${m.program} ${m.pid} carries a harness's child-session mark (${m.markers.join(', ')}): Claude Code started in any shell it opens saves no transcript and gets no mail; replace that window`, detail: m });
421
+ for (const u of unregistered) slow.observations.push({ key: `harness.unregistered:${u.pid}`, family: 'unregistered', def, value: 1, met: { warn: true, crit: true, clears: false }, summary: `Claude Code ${u.pid} on ${u.tty} has no ${u.registry}: it saves nothing and no mail reaches it`, detail: u });
422
+ });
411
423
  if (on.has('services')) await attempt('services', async () => {
412
424
  const list = cfg.services ?? [];
413
425
  const jobs = await this.reader.services(list.map((s) => s.label));
@@ -39,6 +39,10 @@ export const DEFAULTS = {
39
39
  'release.pinned': { forSec: 300 },
40
40
  // a recorded command that cannot run fails its harness on every use (a hook on every tool call): said at once
41
41
  'recorded.broken': { forSec: 0 },
42
+ // a window whose shells start Claude Code as a nested session, and a session that saves nothing: said at once (a
43
+ // Claude Code process is read only once it has run minAgeSec, the time it has to write its registry file)
44
+ 'harness.marked': { forSec: 0 },
45
+ 'harness.unregistered': { forSec: 0, minAgeSec: 120 },
42
46
  'supercode.connector': { forSec: 60 },
43
47
  'supercode.launch': { stuckMin: 5, failedWithinMin: 30 },
44
48
  // one failure that keeps happening (the same command or door with the same outcome, the same mail expiry) in the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-health",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "type": "module",
5
5
  "description": "Optional machine pack: a cheap, deterministic probe of a machine's vitals that mails the machine's maintainer when a threshold is crossed. Each feature sits behind its own permission barrier.",
6
6
  "license": "MIT",
@@ -10,7 +10,8 @@
10
10
  "./format": "./lib/format.mjs",
11
11
  "./paths": "./lib/paths.mjs",
12
12
  "./drift": "./lib/drift.mjs",
13
- "./recorded": "./lib/recorded.mjs"
13
+ "./recorded": "./lib/recorded.mjs",
14
+ "./markers": "./lib/markers.mjs"
14
15
  },
15
16
  "bin": {
16
17
  "supercode-health": "./bin/health.mjs"