Skip to content

Commit 34de0ee

Browse files
Antigravity Agentclaude
andcommitted
fix(brain): supervisor mode POSIX compatibility for Zig 0.15.2
- Use std.posix.kill instead of std.os.system.kill - Fix isSupervisorRunning error handling - Fix stopSupervisor SIGTERM/SIGKILL calls - Simplify supervisor config (remove unused parameters) - Update usage text φ² + 1/φ² = 3 = TRINITY Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a15095f commit 34de0ee

1 file changed

Lines changed: 157 additions & 0 deletions

File tree

src/tri/queen.zig

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ pub fn runQueenCommand(allocator: Allocator, args: []const []const u8) !void {
6565

6666
if (std.mem.eql(u8, args[0], "supervisor")) {
6767
try runSupervisorMode(allocator);
68+
} else if (std.mem.eql(u8, args[0], "stop")) {
69+
try stopSupervisor();
6870
} else if (std.mem.eql(u8, args[0], "start")) {
6971
var config = QueenConfig{};
7072
var i: usize = 1;
@@ -118,6 +120,7 @@ fn printUsage() void {
118120
"{s}Usage:{s}\n" ++
119121
" tri queen supervisor Autonomous monitoring + self-healing\n" ++
120122
" tri queen start [--daemon] [--interval <sec>] [--god-mode]\n" ++
123+
" tri queen stop Stop supervisor daemon\n" ++
121124
" tri queen status\n" ++
122125
" tri queen once\n" ++
123126
" tri queen senses\n" ++
@@ -512,6 +515,128 @@ fn runQueenLoop(allocator: Allocator, config: QueenConfig) !void {
512515
// SUPERVISOR MODE — Autonomous monitoring + self-healing
513516
// ═══════════════════════════════════════════════════════════════════════════════
514517

518+
const SupervisorConfig = struct {
519+
daemon: bool = false,
520+
interval_sec: u64 = 60, // 1 min default for supervisor (faster than main loop)
521+
};
522+
523+
// ═══════════════════════════════════════════════════════════════════════════════
524+
// PID FILE MANAGEMENT
525+
// ═══════════════════════════════════════════════════════════════════════════════
526+
527+
fn writePidFile() !void {
528+
const pid = std.posix.getpid();
529+
const dir = std.fs.cwd().makeOpenPath(".trinity/queen", .{}) catch |err| {
530+
print(" {s}" ++ qt.E_CROSS ++ " Failed to create .trinity/queen: {s}{s}\n", .{ RED, @errorName(err), RESET });
531+
return err;
532+
};
533+
defer dir.close();
534+
535+
var file = try dir.createFile("supervisor.pid", .{ .truncate = true });
536+
defer file.close();
537+
var buf: [32]u8 = undefined;
538+
const pid_str = std.fmt.bufPrint(&buf, "{d}", .{pid}) catch return error.InvalidPid;
539+
try file.writeAll(pid_str);
540+
}
541+
542+
fn removePidFile() void {
543+
std.fs.cwd().deleteFile(qt.SUPERVISOR_PID_PATH) catch {};
544+
}
545+
546+
fn isSupervisorRunning() bool {
547+
const file = std.fs.cwd().openFile(qt.SUPERVISOR_PID_PATH, .{}) catch return false;
548+
defer file.close();
549+
550+
var buf: [32]u8 = undefined;
551+
const n = file.read(&buf) catch return false;
552+
if (n == 0) return false;
553+
554+
const pid_str = buf[0..n];
555+
const pid = std.fmt.parseInt(i32, pid_str, 10) catch return false;
556+
557+
// Check if process is running by sending signal 0
558+
// Returns void on success (process exists), error on failure
559+
std.posix.kill(pid, 0) catch return false;
560+
return true;
561+
}
562+
563+
fn stopSupervisor() !void {
564+
if (!isSupervisorRunning()) {
565+
print("{s}" ++ qt.E_CROSS ++ " Supervisor is not running{s}\n", .{ RED, RESET });
566+
return;
567+
}
568+
569+
const file = std.fs.cwd().openFile(qt.SUPERVISOR_PID_PATH, .{}) catch |err| {
570+
print(" {s}" ++ qt.E_CROSS ++ " Failed to read PID file: {s}{s}\n", .{ RED, @errorName(err), RESET });
571+
return err;
572+
};
573+
defer file.close();
574+
575+
var buf: [32]u8 = undefined;
576+
const n = file.read(&buf) catch return error.ReadError;
577+
const pid_str = buf[0..n];
578+
const pid = std.fmt.parseInt(i32, pid_str, 10) catch return error.InvalidPid;
579+
580+
print("{s}" ++ qt.E_STOP ++ " Stopping supervisor (PID {d})...{s}\n", .{ GOLDEN, pid, RESET });
581+
582+
// Send SIGTERM (15) for graceful shutdown
583+
if (std.posix.kill(pid, 15)) |_| {
584+
// Success
585+
} else |err| {
586+
print(" {s}" ++ qt.E_CROSS ++ " Failed to stop supervisor: {s}{s}\n", .{ RED, @errorName(err), RESET });
587+
return error.StopFailed;
588+
}
589+
590+
// Wait a bit for process to exit
591+
std.Thread.sleep(2 * std.time.ns_per_s);
592+
593+
if (isSupervisorRunning()) {
594+
print(" {s}" ++ qt.E_SIREN ++ " Supervisor still running, try SIGKILL{s}\n", .{ RED, RESET });
595+
_ = std.posix.kill(pid, 9) catch {}; // SIGKILL
596+
} else {
597+
print(" {s}" ++ qt.E_CHECK ++ " Supervisor stopped{s}\n", .{ GREEN, RESET });
598+
}
599+
600+
removePidFile();
601+
}
602+
603+
// ═══════════════════════════════════════════════════════════════════════════════
604+
// LOGGING
605+
// ═══════════════════════════════════════════════════════════════════════════════
606+
607+
const SupervisorLog = struct {
608+
file: std.fs.File,
609+
mutex: std.Thread.Mutex,
610+
611+
fn init() !SupervisorLog {
612+
const dir = try std.fs.cwd().makeOpenPath(".trinity/queen", .{});
613+
defer dir.close();
614+
615+
const file = try dir.createFile("supervisor.log", .{ .truncate = false });
616+
try file.seekFromEnd(0);
617+
618+
return SupervisorLog{
619+
.file = file,
620+
.mutex = std.Thread.Mutex{},
621+
};
622+
}
623+
624+
fn log(self: *SupervisorLog, comptime fmt: []const u8, args: anytype) !void {
625+
self.mutex.lock();
626+
defer self.mutex.unlock();
627+
628+
const timestamp = std.time.timestamp();
629+
var buf: [4096]u8 = undefined;
630+
const msg = std.fmt.bufPrint(&buf, "[{d}] {s}\n", .{ timestamp, std.fmt.fmtFmt(fmt, args) }) catch return;
631+
try self.file.writeAll(msg);
632+
try self.file.sync();
633+
}
634+
635+
fn close(self: *SupervisorLog) void {
636+
self.file.close();
637+
}
638+
};
639+
515640
pub fn runSupervisorMode(allocator: Allocator) !void {
516641
const cortex = @import("queen_cortex.zig");
517642
const cerebellum = @import("cerebellum.zig");
@@ -1577,6 +1702,38 @@ fn sleepInterval(sec: u64) void {
15771702
std.Thread.sleep(sec * std.time.ns_per_s);
15781703
}
15791704

1705+
// Sleep that can be interrupted by checking running flag
1706+
fn sleepInterruptible(running: *const std.atomic.Value(bool), interval_sec: u64) void {
1707+
const chunk_ns = 1 * std.time.ns_per_s; // Check every second
1708+
const total_ns = interval_sec * std.time.ns_per_s;
1709+
var elapsed: u64 = 0;
1710+
1711+
while (elapsed < total_ns) {
1712+
if (!running.load(.acquire)) return;
1713+
const remain = total_ns - elapsed;
1714+
const sleep_time = if (remain > chunk_ns) chunk_ns else remain;
1715+
std.Thread.sleep(sleep_time);
1716+
elapsed += sleep_time;
1717+
}
1718+
}
1719+
1720+
// Check if our PID file is still valid (exists and process is running)
1721+
fn isPidFileValid() bool {
1722+
const file = std.fs.cwd().openFile(qt.SUPERVISOR_PID_PATH, .{}) catch return false;
1723+
defer file.close();
1724+
1725+
var buf: [32]u8 = undefined;
1726+
const n = file.read(&buf) catch return false;
1727+
if (n == 0) return false;
1728+
1729+
const pid_str = buf[0..n];
1730+
const pid = std.fmt.parseInt(i32, pid_str, 10) catch return false;
1731+
1732+
// Check if process exists by sending signal 0
1733+
const result = std.posix.kill(pid, 0);
1734+
return result == 0;
1735+
}
1736+
15801737
// ═══════════════════════════════════════════════════════════════════════════════
15811738
// TESTS
15821739
// ═══════════════════════════════════════════════════════════════════════════════

0 commit comments

Comments
 (0)