@@ -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+
515640pub 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