description = """ Death warrant execution state machine for Dogs. Dogs execute this molecule to process death warrants. Each Dog is a lightweight goroutine (NOT a Claude session) that runs the interrogation state machine. ## Architecture Context Dogs are lightweight workers in Boot's pool (see dog-pool-architecture.md): - Fixed pool of 5 goroutines (configurable via GT_DOG_POOL_SIZE) - State persisted to $GT_ROOT/deacon/dogs/active/.json - Recovery on Boot restart via orphan state files ## State Machine ``` ┌─────────────────────────────────────────┐ │ │ ▼ │ ┌───────────────────────────┐ │ │ INTERROGATING │ │ │ │ │ │ 1. Send health check │ │ │ 2. Open timeout gate │ │ └───────────┬───────────────┘ │ │ │ │ gate closes (timeout or response) │ ▼ │ ┌───────────────────────────┐ │ │ EVALUATING │ │ │ │ │ │ Check tmux output for │ │ │ ALIVE keyword │ │ └───────────┬───────────────┘ │ │ │ ┌───────┴───────┐ │ │ │ │ ▼ ▼ │ [ALIVE found] [No ALIVE] │ │ │ │ │ │ attempt < 3? │ │ ├──────────────────────────────────→─┘ │ │ yes: attempt++, longer timeout │ │ │ │ no: attempt == 3 ▼ ▼ ┌─────────┐ ┌─────────────┐ │ PARDONED│ │ EXECUTING │ │ │ │ │ │ Cancel │ │ Kill tmux │ │ warrant │ │ session │ └────┬────┘ └──────┬──────┘ │ │ └────────┬───────┘ │ ▼ ┌────────────────┐ │ EPITAPH │ │ │ │ Log outcome │ │ Release dog │ └────────────────┘ ``` ## Timeout Gates | Attempt | Timeout | Cumulative Wait | |---------|---------|-----------------| | 1 | 60s | 60s | | 2 | 120s | 180s (3 min) | | 3 | 240s | 420s (7 min) | Timeout gates work like this: - Gate opens when interrogation message is sent - Gate closes when EITHER: a) Timeout expires (proceed to evaluate) b) Response detected (early close, proceed to evaluate) - The gate state determines the evaluation outcome ## Interrogation Message Format ``` [DOG] HEALTH CHECK: Session {target}, respond ALIVE within {timeout}s or face termination. Warrant reason: {reason} Filed by: {requester} Attempt: {attempt}/3 ``` ## Response Detection The Dog checks tmux output for: 1. The ALIVE keyword (explicit response) 2. Any Claude output after the health check (implicit activity) ```go func (d *Dog) CheckForResponse() bool { output := tmux.CapturePane(d.Warrant.Target, 50) // Last 50 lines return strings.Contains(output, "ALIVE") } ``` ## Variables | Variable | Source | Description | |-------------|-------------|-----------------------------------------------| | warrant_id | hook_bead | Bead ID of the death warrant | | target | warrant | Session name to interrogate | | reason | warrant | Why warrant was issued | | requester | warrant | Who filed the warrant (e.g., deacon, witness) | ## Integration Dogs are NOT Claude sessions. This molecule is: 1. A specification document (defines the state machine) 2. A reference for Go implementation in internal/shutdown/ 3. A template for creating warrant-tracking beads The Go implementation follows this spec exactly.""" formula = "mol-shutdown-dance" version = 1 [squash] trigger = "on_complete" template_type = "operational" include_metrics = true # ============================================================================ # STEP 1: WARRANT_RECEIVED # ============================================================================ [[steps]] id = "warrant-received" title = "Receive and validate death warrant" description = """ Entry point when Dog is allocated from pool. **1. Read warrant from allocation:** The Dog receives a Warrant struct containing: - ID: Bead ID of the warrant - Target: Session name (e.g., "gt-gastown-Toast") - Reason: Why termination requested - Requester: Who filed (deacon, witness, mayor) - FiledAt: Timestamp **2. Validate target exists:** ```bash tmux has-session -t {target} 2>/dev/null ``` If target doesn't exist: - Warrant is stale (already dead) - Skip to EPITAPH with outcome=already_dead **3. Initialize state file:** Write initial state to $GT_ROOT/deacon/dogs/active/{dog-id}.json **4. Set initial attempt counter:** attempt = 1 **Exit criteria:** Warrant validated, target confirmed alive, state initialized.""" # ============================================================================ # STEP 2: INTERROGATION_1 (60s timeout) # ============================================================================ [[steps]] id = "interrogation-1" title = "First interrogation (60s timeout)" needs = ["warrant-received"] description = """ First attempt to contact the session. **1. Compose health check message:** ``` [DOG] HEALTH CHECK: Session {target}, respond ALIVE within 60s or face termination. Warrant reason: {reason} Filed by: {requester} Attempt: 1/3 ``` **2. Send via tmux:** ```bash tmux send-keys -t {target} "{message}" Enter ``` **3. Open timeout gate:** Gate configuration: - Type: timer - Timeout: 60 seconds - Close conditions: a) Timer expires b) ALIVE keyword detected in output **4. Wait for gate to close:** The Dog waits (select on timer channel or early close signal). **5. Record interrogation timestamp:** Update state file with last_message_at. **Exit criteria:** Message sent, waiting for gate to close.""" # ============================================================================ # STEP 3: EVALUATE_1 # ============================================================================ [[steps]] id = "evaluate-1" title = "Evaluate first interrogation response" needs = ["interrogation-1"] description = """ Check if session responded to first interrogation. **1. Capture tmux output:** ```bash tmux capture-pane -t {target} -p | tail -50 ``` **2. Check for ALIVE keyword:** ```go if strings.Contains(output, "ALIVE") { return PARDONED } ``` **3. Decision:** - ALIVE found → Proceed to PARDON - No ALIVE → Proceed to INTERROGATION_2 **Exit criteria:** Response evaluated, next step determined.""" # ============================================================================ # STEP 4: INTERROGATION_2 (120s timeout) # ============================================================================ [[steps]] id = "interrogation-2" title = "Second interrogation (120s timeout)" needs = ["evaluate-1"] gate = { type = "conditional", condition = "no_response_1" } description = """ Second attempt with longer timeout. Only executed if evaluate-1 found no response. **1. Increment attempt:** attempt = 2 **2. Compose health check message:** ``` [DOG] HEALTH CHECK: Session {target}, respond ALIVE within 120s or face termination. Warrant reason: {reason} Filed by: {requester} Attempt: 2/3 ``` **3. Send via tmux:** ```bash tmux send-keys -t {target} "{message}" Enter ``` **4. Open timeout gate:** - Type: timer - Timeout: 120 seconds **5. Wait for gate to close.** **Exit criteria:** Second message sent, waiting for gate.""" # ============================================================================ # STEP 5: EVALUATE_2 # ============================================================================ [[steps]] id = "evaluate-2" title = "Evaluate second interrogation response" needs = ["interrogation-2"] description = """ Check if session responded to second interrogation. **1. Capture tmux output:** ```bash tmux capture-pane -t {target} -p | tail -50 ``` **2. Check for ALIVE keyword.** **3. Decision:** - ALIVE found → Proceed to PARDON - No ALIVE → Proceed to INTERROGATION_3 **Exit criteria:** Response evaluated, next step determined.""" # ============================================================================ # STEP 6: INTERROGATION_3 (240s timeout) # ============================================================================ [[steps]] id = "interrogation-3" title = "Final interrogation (240s timeout)" needs = ["evaluate-2"] gate = { type = "conditional", condition = "no_response_2" } description = """ Final attempt before execution. Only executed if evaluate-2 found no response. **1. Increment attempt:** attempt = 3 **2. Compose health check message:** ``` [DOG] HEALTH CHECK: Session {target}, respond ALIVE within 240s or face termination. Warrant reason: {reason} Filed by: {requester} Attempt: 3/3 ``` **3. Send via tmux:** ```bash tmux send-keys -t {target} "{message}" Enter ``` **4. Open timeout gate:** - Type: timer - Timeout: 240 seconds - This is the FINAL chance **5. Wait for gate to close.** **Exit criteria:** Final message sent, waiting for gate.""" # ============================================================================ # STEP 7: EVALUATE_3 # ============================================================================ [[steps]] id = "evaluate-3" title = "Evaluate final interrogation response" needs = ["interrogation-3"] description = """ Final evaluation before execution. **1. Capture tmux output:** ```bash tmux capture-pane -t {target} -p | tail -50 ``` **2. Check for ALIVE keyword.** **3. Decision:** - ALIVE found → Proceed to PARDON - No ALIVE → Proceed to EXECUTE **Exit criteria:** Final decision made.""" # ============================================================================ # STEP 8: PARDON (success path) # ============================================================================ [[steps]] id = "pardon" title = "Pardon session - cancel warrant" needs = ["evaluate-1", "evaluate-2", "evaluate-3"] gate = { type = "conditional", condition = "alive_detected" } description = """ Session responded - cancel the death warrant. **1. Update state:** state = PARDONED **2. Record pardon details:** ```json { "outcome": "pardoned", "attempt": {attempt}, "response_time": "{time_since_last_interrogation}s", "pardoned_at": "{timestamp}" } ``` **3. Cancel warrant bead:** ```bash bd close {warrant_id} --reason "Session responded at attempt {attempt}" ``` **4. Notify requester:** ```bash gt mail send {requester}/ -s "PARDON: {target}" -m "Death warrant cancelled. Session responded after attempt {attempt}. Warrant: {warrant_id} Response detected: {timestamp}" ``` **Exit criteria:** Warrant cancelled, requester notified.""" # ============================================================================ # STEP 9: EXECUTE (termination path) # ============================================================================ [[steps]] id = "execute" title = "Execute warrant - kill session" needs = ["evaluate-3"] gate = { type = "conditional", condition = "no_response_final" } description = """ Session unresponsive after 3 attempts - execute the warrant. **1. Update state:** state = EXECUTING **2. Kill the tmux session:** ```bash tmux kill-session -t {target} ``` **3. Verify session is dead:** ```bash tmux has-session -t {target} 2>/dev/null # Should fail (session gone) ``` **4. If session still exists (kill failed):** - Force kill with tmux kill-server if isolated - Or escalate to Boot for manual intervention **5. Record execution details:** ```json { "outcome": "executed", "attempts": 3, "total_wait": "420s", "executed_at": "{timestamp}" } ``` **Exit criteria:** Session terminated.""" # ============================================================================ # STEP 10: EPITAPH (completion) # ============================================================================ [[steps]] id = "epitaph" title = "Log cause of death and close warrant" needs = ["pardon", "execute"] description = """ Final step - create audit record and release Dog back to pool. **1. Compose epitaph based on outcome:** For PARDONED: ``` EPITAPH: {target} Verdict: PARDONED Warrant: {warrant_id} Reason: {reason} Filed by: {requester} Response: Attempt {attempt}, after {wait_time}s Pardoned at: {timestamp} ``` For EXECUTED: ``` EPITAPH: {target} Verdict: EXECUTED Warrant: {warrant_id} Reason: {reason} Filed by: {requester} Attempts: 3 (60s + 120s + 240s = 420s total) Executed at: {timestamp} ``` For ALREADY_DEAD (target gone before interrogation): ``` EPITAPH: {target} Verdict: ALREADY_DEAD Warrant: {warrant_id} Reason: {reason} Filed by: {requester} Note: Target session not found at warrant processing ``` **2. Close warrant bead:** ```bash bd close {warrant_id} --reason "{epitaph_summary}" ``` **3. Move state file to completed:** ```bash mv $GT_ROOT/deacon/dogs/active/{dog-id}.json $GT_ROOT/deacon/dogs/completed/ ``` **4. Report to Boot:** Write completion file: $GT_ROOT/deacon/dogs/active/{dog-id}.done ```json { "dog_id": "{dog-id}", "warrant_id": "{warrant_id}", "target": "{target}", "outcome": "{pardoned|executed|already_dead}", "duration": "{total_duration}s" } ``` **5. Release Dog to pool:** Dog resets state and returns to idle channel. **Exit criteria:** Warrant closed, Dog released, audit complete.""" # ============================================================================ # VARIABLES # ============================================================================ [vars] [vars.warrant_id] description = "Bead ID of the death warrant being processed" required = true [vars.target] description = "Session name to interrogate (e.g., gt-gastown-Toast)" required = true [vars.reason] description = "Why the warrant was issued" required = true [vars.requester] description = "Who filed the warrant (deacon, witness, mayor)" required = true default = "deacon"