@@ -205,6 +205,35 @@ Examples:
205205 RunE : runDeaconStaleHooks ,
206206}
207207
208+ var deaconPauseCmd = & cobra.Command {
209+ Use : "pause" ,
210+ Short : "Pause the Deacon to prevent patrol actions" ,
211+ Long : `Pause the Deacon to prevent it from performing any patrol actions.
212+
213+ When paused, the Deacon:
214+ - Will not create patrol molecules
215+ - Will not run health checks
216+ - Will not take any autonomous actions
217+ - Will display a PAUSED message on startup
218+
219+ The pause state persists across session restarts. Use 'gt deacon resume'
220+ to allow the Deacon to work again.
221+
222+ Examples:
223+ gt deacon pause # Pause with no reason
224+ gt deacon pause --reason="testing" # Pause with a reason` ,
225+ RunE : runDeaconPause ,
226+ }
227+
228+ var deaconResumeCmd = & cobra.Command {
229+ Use : "resume" ,
230+ Short : "Resume the Deacon to allow patrol actions" ,
231+ Long : `Resume the Deacon so it can perform patrol actions again.
232+
233+ This removes the pause file and allows the Deacon to work normally.` ,
234+ RunE : runDeaconResume ,
235+ }
236+
208237var (
209238 triggerTimeout time.Duration
210239
@@ -220,6 +249,9 @@ var (
220249 // Stale hooks flags
221250 staleHooksMaxAge time.Duration
222251 staleHooksDryRun bool
252+
253+ // Pause flags
254+ pauseReason string
223255)
224256
225257func init () {
@@ -234,6 +266,8 @@ func init() {
234266 deaconCmd .AddCommand (deaconForceKillCmd )
235267 deaconCmd .AddCommand (deaconHealthStateCmd )
236268 deaconCmd .AddCommand (deaconStaleHooksCmd )
269+ deaconCmd .AddCommand (deaconPauseCmd )
270+ deaconCmd .AddCommand (deaconResumeCmd )
237271
238272 // Flags for trigger-pending
239273 deaconTriggerPendingCmd .Flags ().DurationVar (& triggerTimeout , "timeout" , 2 * time .Second ,
@@ -259,6 +293,10 @@ func init() {
259293 deaconStaleHooksCmd .Flags ().BoolVar (& staleHooksDryRun , "dry-run" , false ,
260294 "Preview what would be unhooked without making changes" )
261295
296+ // Flags for pause
297+ deaconPauseCmd .Flags ().StringVar (& pauseReason , "reason" , "" ,
298+ "Reason for pausing the Deacon" )
299+
262300 deaconStartCmd .Flags ().StringVar (& deaconAgentOverride , "agent" , "" , "Agent alias to run the Deacon with (overrides town default)" )
263301 deaconAttachCmd .Flags ().StringVar (& deaconAgentOverride , "agent" , "" , "Agent alias to run the Deacon with (overrides town default)" )
264302 deaconRestartCmd .Flags ().StringVar (& deaconAgentOverride , "agent" , "" , "Agent alias to run the Deacon with (overrides town default)" )
@@ -418,6 +456,23 @@ func runDeaconStatus(cmd *cobra.Command, args []string) error {
418456
419457 sessionName := getDeaconSessionName ()
420458
459+ // Check pause state first (most important)
460+ townRoot , _ := workspace .FindFromCwdOrError ()
461+ if townRoot != "" {
462+ paused , state , err := deacon .IsPaused (townRoot )
463+ if err == nil && paused {
464+ fmt .Printf ("%s DEACON PAUSED\n " , style .Bold .Render ("⏸️" ))
465+ if state .Reason != "" {
466+ fmt .Printf (" Reason: %s\n " , state .Reason )
467+ }
468+ fmt .Printf (" Paused at: %s\n " , state .PausedAt .Format (time .RFC3339 ))
469+ fmt .Printf (" Paused by: %s\n " , state .PausedBy )
470+ fmt .Println ()
471+ fmt .Printf ("Resume with: %s\n " , style .Dim .Render ("gt deacon resume" ))
472+ fmt .Println ()
473+ }
474+ }
475+
421476 running , err := t .HasSession (sessionName )
422477 if err != nil {
423478 return fmt .Errorf ("checking session: %w" , err )
@@ -487,6 +542,19 @@ func runDeaconHeartbeat(cmd *cobra.Command, args []string) error {
487542 return fmt .Errorf ("not in a Gas Town workspace: %w" , err )
488543 }
489544
545+ // Check if Deacon is paused - if so, refuse to update heartbeat
546+ paused , state , err := deacon .IsPaused (townRoot )
547+ if err != nil {
548+ return fmt .Errorf ("checking pause state: %w" , err )
549+ }
550+ if paused {
551+ fmt .Printf ("%s Deacon is paused. Use 'gt deacon resume' to unpause.\n " , style .Bold .Render ("⏸️" ))
552+ if state .Reason != "" {
553+ fmt .Printf (" Reason: %s\n " , state .Reason )
554+ }
555+ return errors .New ("Deacon is paused" )
556+ }
557+
490558 action := ""
491559 if len (args ) > 0 {
492560 action = strings .Join (args , " " )
@@ -951,3 +1019,68 @@ func runDeaconStaleHooks(cmd *cobra.Command, args []string) error {
9511019
9521020 return nil
9531021}
1022+
1023+ // runDeaconPause pauses the Deacon to prevent patrol actions.
1024+ func runDeaconPause (cmd * cobra.Command , args []string ) error {
1025+ townRoot , err := workspace .FindFromCwdOrError ()
1026+ if err != nil {
1027+ return fmt .Errorf ("not in a Gas Town workspace: %w" , err )
1028+ }
1029+
1030+ // Check if already paused
1031+ paused , state , err := deacon .IsPaused (townRoot )
1032+ if err != nil {
1033+ return fmt .Errorf ("checking pause state: %w" , err )
1034+ }
1035+ if paused {
1036+ fmt .Printf ("%s Deacon is already paused\n " , style .Dim .Render ("○" ))
1037+ fmt .Printf (" Reason: %s\n " , state .Reason )
1038+ fmt .Printf (" Paused at: %s\n " , state .PausedAt .Format (time .RFC3339 ))
1039+ fmt .Printf (" Paused by: %s\n " , state .PausedBy )
1040+ return nil
1041+ }
1042+
1043+ // Pause the Deacon
1044+ if err := deacon .Pause (townRoot , pauseReason , "human" ); err != nil {
1045+ return fmt .Errorf ("pausing Deacon: %w" , err )
1046+ }
1047+
1048+ fmt .Printf ("%s Deacon paused\n " , style .Bold .Render ("⏸️" ))
1049+ if pauseReason != "" {
1050+ fmt .Printf (" Reason: %s\n " , pauseReason )
1051+ }
1052+ fmt .Printf (" Pause file: %s\n " , deacon .GetPauseFile (townRoot ))
1053+ fmt .Println ()
1054+ fmt .Printf ("The Deacon will not perform any patrol actions until resumed.\n " )
1055+ fmt .Printf ("Resume with: %s\n " , style .Dim .Render ("gt deacon resume" ))
1056+
1057+ return nil
1058+ }
1059+
1060+ // runDeaconResume resumes the Deacon to allow patrol actions.
1061+ func runDeaconResume (cmd * cobra.Command , args []string ) error {
1062+ townRoot , err := workspace .FindFromCwdOrError ()
1063+ if err != nil {
1064+ return fmt .Errorf ("not in a Gas Town workspace: %w" , err )
1065+ }
1066+
1067+ // Check if paused
1068+ paused , _ , err := deacon .IsPaused (townRoot )
1069+ if err != nil {
1070+ return fmt .Errorf ("checking pause state: %w" , err )
1071+ }
1072+ if ! paused {
1073+ fmt .Printf ("%s Deacon is not paused\n " , style .Dim .Render ("○" ))
1074+ return nil
1075+ }
1076+
1077+ // Resume the Deacon
1078+ if err := deacon .Resume (townRoot ); err != nil {
1079+ return fmt .Errorf ("resuming Deacon: %w" , err )
1080+ }
1081+
1082+ fmt .Printf ("%s Deacon resumed\n " , style .Bold .Render ("▶️" ))
1083+ fmt .Println ("The Deacon can now perform patrol actions." )
1084+
1085+ return nil
1086+ }
0 commit comments