repos / zmx

session persistence for terminal processes
git clone https://github.com/neurosnap/zmx.git

commit
c1c7f7b
parent
0ce812c
author
Eric Bower
date
2026-04-29 09:46:08 -0400 EDT
feat(wait): summary of failed tasks and actionable commands to run

This simply prints the failed tasks and shows some commands to run to see what
went wrong.
1 files changed,  +46, -13
M src/main.zig
+46, -13
  1@@ -1477,11 +1477,12 @@ fn wait(cfg: *Cfg, matchers: std.ArrayList(SessionMatch)) !void {
  2     var max_seen: i32 = 0;
  3     var zero_match_iters: u32 = 0;
  4 
  5+    var agg_exit_code: u8 = 0;
  6     while (true) {
  7+        agg_exit_code = 0;
  8         var sessions = try util.get_session_entries(alloc, cfg.socket_dir);
  9         var total: i32 = 0;
 10         var done: i32 = 0;
 11-        var agg_exit_code: u8 = 0;
 12 
 13         for (sessions.items) |session| {
 14             var found = false;
 15@@ -1502,8 +1503,8 @@ fn wait(cfg: *Cfg, matchers: std.ArrayList(SessionMatch)) !void {
 16                 // persist as task_ended_at==0 forever → infinite "still
 17                 // waiting". Count it as done+failed so wait terminates.
 18                 try stderr.print(
 19-                    "task unreachable: {s} ({s})\n",
 20-                    .{ session.name, session.error_name orelse "unknown" },
 21+                    "[{d}] task unreachable: {s} ({s})\n",
 22+                    .{ std.time.timestamp(), session.name, session.error_name orelse "unknown" },
 23                 );
 24                 try stderr.flush();
 25                 agg_exit_code = 1;
 26@@ -1511,11 +1512,17 @@ fn wait(cfg: *Cfg, matchers: std.ArrayList(SessionMatch)) !void {
 27                 continue;
 28             }
 29             if (session.task_ended_at == 0) {
 30-                try stdout.print("waiting task={s}\n", .{session.name});
 31+                try stdout.print(
 32+                    "[{d}] waiting task={s}\n",
 33+                    .{ std.time.timestamp(), session.name },
 34+                );
 35                 try stdout.flush();
 36                 continue;
 37             }
 38-            try stdout.print("completed task={s} exit_code={d}\n", .{ session.name, session.task_exit_code.? });
 39+            try stdout.print(
 40+                "[{d}] completed task={s} exit_code={d}\n",
 41+                .{ session.task_ended_at.?, session.name, session.task_exit_code.? },
 42+            );
 43             try stdout.flush();
 44             if (session.task_exit_code != 0) {
 45                 agg_exit_code = session.task_exit_code orelse 0;
 46@@ -1543,14 +1550,7 @@ fn wait(cfg: *Cfg, matchers: std.ArrayList(SessionMatch)) !void {
 47         max_seen = total;
 48 
 49         if (total > 0 and total == done) {
 50-            if (agg_exit_code == 0) {
 51-                try stdout.print("task(s) completed!\n", .{});
 52-            } else {
 53-                try stdout.print("task(s) failed!\n", .{});
 54-            }
 55-            try stdout.flush();
 56-            std.process.exit(agg_exit_code);
 57-            return;
 58+            break;
 59         }
 60 
 61         if (max_seen == 0) {
 62@@ -1569,6 +1569,39 @@ fn wait(cfg: *Cfg, matchers: std.ArrayList(SessionMatch)) !void {
 63 
 64         std.Thread.sleep(1000 * std.time.ns_per_ms);
 65     }
 66+
 67+    if (agg_exit_code == 0) {
 68+        try stdout.print("task(s) completed!\n", .{});
 69+    } else {
 70+        try stdout.print("task(s) failed!\n", .{});
 71+    }
 72+    try stdout.flush();
 73+
 74+    const sessions = try util.get_session_entries(alloc, cfg.socket_dir);
 75+    for (sessions.items) |session| {
 76+        var found = false;
 77+        for (matchers.items) |m| {
 78+            if (m.matches(session.name)) {
 79+                found = true;
 80+                break;
 81+            }
 82+        }
 83+        if (!found) {
 84+            continue;
 85+        }
 86+        if (session.task_exit_code.? > 0) {
 87+            try stdout.print("---\n", .{});
 88+            try stdout.print("[{d}] failed task={s} exit_status={d}\n\n", .{
 89+                session.task_ended_at.?,
 90+                session.name,
 91+                session.task_exit_code.?,
 92+            });
 93+            try stdout.print("See the logs:\nzmx history {s}\nzmx attach {s}\n", .{ session.name, session.name });
 94+            try stdout.flush();
 95+        }
 96+    }
 97+
 98+    std.process.exit(agg_exit_code);
 99 }
100 
101 fn list(cfg: *Cfg, short: bool) !void {