feat(deacon): improve timing and add heartbeat command

Timing changes for more relaxed poke intervals:
- Daemon heartbeat: 60s → 5 minutes
- Backoff base: 60s → 5 minutes
- Backoff max: 10m → 30 minutes
- Fresh threshold: <2min → <5min
- Stale threshold: 2-5min → 5-15min
- Very stale threshold: >5min → >15min

New command:
- `gt deacon heartbeat [action]` - Touch heartbeat file easily

Template rewrite:
- Clearer wake/sleep model
- Documents wake sources (daemon poke, mail, timer callbacks)
- Simpler rounds with `gt deacon heartbeat` instead of bash echo
- Mentions plugins as optional maintenance tasks
- Explains timer callbacks pattern

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-20 02:12:21 -08:00
parent 348a7d0525
commit 1554380228
9 changed files with 195 additions and 161 deletions

View File

@@ -85,26 +85,26 @@ func (hb *Heartbeat) Age() time.Duration {
return time.Since(hb.Timestamp)
}
// IsFresh returns true if the heartbeat is less than 2 minutes old.
// A fresh heartbeat means the Deacon is actively working.
// IsFresh returns true if the heartbeat is less than 5 minutes old.
// A fresh heartbeat means the Deacon is actively working or recently finished.
func (hb *Heartbeat) IsFresh() bool {
return hb != nil && hb.Age() < 2*time.Minute
return hb != nil && hb.Age() < 5*time.Minute
}
// IsStale returns true if the heartbeat is 2-5 minutes old.
// A stale heartbeat may indicate the Deacon is slow or stuck.
// IsStale returns true if the heartbeat is 5-15 minutes old.
// A stale heartbeat may indicate the Deacon is doing a long operation.
func (hb *Heartbeat) IsStale() bool {
if hb == nil {
return false
}
age := hb.Age()
return age >= 2*time.Minute && age < 5*time.Minute
return age >= 5*time.Minute && age < 15*time.Minute
}
// IsVeryStale returns true if the heartbeat is more than 5 minutes old.
// IsVeryStale returns true if the heartbeat is more than 15 minutes old.
// A very stale heartbeat means the Deacon should be poked.
func (hb *Heartbeat) IsVeryStale() bool {
return hb == nil || hb.Age() >= 5*time.Minute
return hb == nil || hb.Age() >= 15*time.Minute
}
// ShouldPoke returns true if the daemon should poke the Deacon.