From ce9cd72c377e6f7c7a8861b27f4ec3dcefd27738 Mon Sep 17 00:00:00 2001 From: furiosa Date: Sun, 11 Jan 2026 10:55:10 -0500 Subject: [PATCH 1/6] docs: clarify Beads issue ID format in README (gt-uzx2c) Added "Issue IDs" section to Core Concepts explaining that Gas Town uses Beads' auto-generated short IDs (e.g., gt-x7k2m) rather than sequential numbers like GitHub issues. Updated all example issue IDs throughout the README to use realistic Beads-style IDs instead of confusing "issue-123" format. Fixes: GitHub #309 --- README.md | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e9e39ed1..da6dbf13 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,27 @@ Work tracking units. Bundle multiple issues/tasks that get assigned to agents. Git-backed issue tracking system that stores work state as structured data. +### Issue IDs 🔢 + +Gas Town uses [Beads](https://github.com/steveyegge/beads) for issue tracking, which generates **short random IDs** - not sequential numbers like GitHub issues. + +``` +gt-x7k2m ← Gas Town issue (gt prefix) +bd-9np4q ← Beads issue (bd prefix) +myproj-abc3 ← Your project (custom prefix) +``` + +The prefix comes from your project's Beads configuration. Find yours with `bd config get prefix`. + +**Common commands:** +```bash +bd list # List all issues +bd show gt-x7k2m # Show issue details +bd ready # Issues ready for work +``` + +> **Note:** These are Beads IDs, not GitHub issue numbers. GitHub issue #309 might have Beads ID `gt-uzx2c`. + > **New to Gas Town?** See the [Glossary](docs/glossary.md) for a complete guide to terminology and concepts. ## Installation @@ -142,10 +163,10 @@ sequenceDiagram gt mayor attach # 2. In Mayor session, create a convoy -gt convoy create "Feature X" issue-123 issue-456 --notify --human +gt convoy create "Feature X" gt-x7k2m gt-9np4q --notify --human # 3. Assign work to an agent -gt sling issue-123 myproject +gt sling gt-x7k2m myproject # 4. Track progress gt convoy list @@ -177,7 +198,7 @@ flowchart LR gt mayor attach # In Mayor, create convoy and let it orchestrate -gt convoy create "Auth System" issue-101 issue-102 --notify +gt convoy create "Auth System" gt-a3k7x gt-b2m9y --notify # Track progress gt convoy list @@ -188,8 +209,8 @@ gt convoy list Run individual runtime instances manually. Gas Town just tracks state. ```bash -gt convoy create "Fix bugs" issue-123 # Create convoy (sling auto-creates if skipped) -gt sling issue-123 myproject # Assign to worker +gt convoy create "Fix bugs" gt-f4n8p # Create convoy (sling auto-creates if skipped) +gt sling gt-f4n8p myproject # Assign to worker claude --resume # Agent reads mail, runs work (Claude) # or: codex # Start Codex in the workspace gt convoy list # Check progress @@ -264,10 +285,10 @@ bd mol pour release --var version=1.2.0 gt convoy create "Bug Fixes" --human # Add issues -gt convoy add-issue bug-101 bug-102 +gt convoy add-issue gt-c5p2q gt-d8r3s # Assign to specific agents -gt sling bug-101 myproject/my-agent +gt sling gt-c5p2q myproject/my-agent # Check status gt convoy show From 54be24ab5bce9b84612d845a16bb3bc8565c31a0 Mon Sep 17 00:00:00 2001 From: nux Date: Sun, 11 Jan 2026 11:01:18 -0500 Subject: [PATCH 2/6] docs(keepalive): document nil sentinel pattern Add comprehensive godoc comments explaining how the sentinel pattern enables graceful degradation when keepalive files are missing or stale. --- internal/keepalive/keepalive.go | 40 ++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/internal/keepalive/keepalive.go b/internal/keepalive/keepalive.go index 69d79716..9a1137cf 100644 --- a/internal/keepalive/keepalive.go +++ b/internal/keepalive/keepalive.go @@ -10,6 +10,26 @@ // Functions in this package write JSON files to .runtime/ or daemon/ directories. // These files are used by the daemon to detect agent activity and implement // features like exponential backoff during idle periods. +// +// # Sentinel Pattern +// +// This package uses the nil sentinel pattern for graceful degradation: +// +// - [Read] returns nil when the keepalive file doesn't exist or can't be parsed, +// rather than returning an error. This allows callers to treat "no signal" +// and "stale signal" uniformly. +// +// - [State.Age] accepts nil receivers and returns a sentinel duration of 365 days, +// which is guaranteed to exceed any reasonable staleness threshold. This enables +// simple threshold checks without nil guards: +// +// state := keepalive.Read(root) +// if state.Age() > 5*time.Minute { +// // Agent is idle or keepalive missing - both handled the same way +// } +// +// The sentinel approach simplifies daemon logic by eliminating error-handling +// branches for the common case of missing or stale keepalives. package keepalive import ( @@ -76,7 +96,10 @@ func TouchInWorkspace(workspaceRoot, command string) { } // Read returns the current keepalive state for the workspace. -// Returns nil if the file doesn't exist or can't be read. +// +// This function uses the nil sentinel pattern: it returns nil (not an error) +// when the keepalive file doesn't exist, can't be read, or contains invalid JSON. +// Callers can safely pass the result to [State.Age] without nil checks. func Read(workspaceRoot string) *State { keepalivePath := filepath.Join(workspaceRoot, ".runtime", "keepalive.json") @@ -94,10 +117,21 @@ func Read(workspaceRoot string) *State { } // Age returns how old the keepalive signal is. -// Returns a very large duration if the state is nil. +// +// This method implements the sentinel pattern by accepting nil receivers. +// When s is nil (indicating no keepalive exists), it returns 365 days—a value +// guaranteed to exceed any reasonable staleness threshold. This allows callers +// to write simple threshold checks without nil guards: +// +// if keepalive.Read(root).Age() > 5*time.Minute { ... } +// +// The 365-day sentinel was chosen because: +// - It exceeds any practical idle timeout (typically seconds to minutes) +// - It's semantically "infinitely old" for activity detection purposes +// - It avoids magic values like MaxInt64 that could cause overflow issues func (s *State) Age() time.Duration { if s == nil { - return 24 * time.Hour * 365 // No keepalive + return 24 * time.Hour * 365 // Sentinel: treat missing keepalive as maximally stale } return time.Since(s.Timestamp) } From 96632fe4ba7e952913e5ca332ad768f5e3b5637b Mon Sep 17 00:00:00 2001 From: dementus Date: Sun, 11 Jan 2026 11:00:57 -0500 Subject: [PATCH 3/6] docs: add godoc for formatInt in activity package Explains the integer-to-string conversion behavior: - Direct rune conversion for single digits (efficiency) - Iterative digit extraction for larger numbers - Avoids strconv import for simple formatting --- internal/activity/activity.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/activity/activity.go b/internal/activity/activity.go index 9a4cfd66..80c45e43 100644 --- a/internal/activity/activity.go +++ b/internal/activity/activity.go @@ -92,6 +92,10 @@ func formatDays(d time.Duration) string { return formatInt(days) + "d" } +// formatInt converts a non-negative integer to its decimal string representation. +// For single digits (0-9), it uses direct rune conversion for efficiency. +// For larger numbers, it extracts digits iteratively from least to most significant. +// This avoids importing strconv for simple integer formatting in the activity package. func formatInt(n int) string { if n < 10 { return string(rune('0'+n)) From 66f6e378444a8352f332bbbd6488f61e6e9f2cb2 Mon Sep 17 00:00:00 2001 From: rictus Date: Sun, 11 Jan 2026 11:00:45 -0500 Subject: [PATCH 4/6] docs: add godoc for isAutonomousRole explaining autonomous roles Explains that autonomous roles (polecat, witness, refinery, deacon) get automatic mail injection on startup since they operate without human prompting. Non-autonomous roles (mayor, crew) skip this. Closes: gt-pawy3 --- internal/runtime/runtime.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/internal/runtime/runtime.go b/internal/runtime/runtime.go index e26be5f0..c27fd9b8 100644 --- a/internal/runtime/runtime.go +++ b/internal/runtime/runtime.go @@ -84,6 +84,13 @@ func RunStartupFallback(t *tmux.Tmux, sessionID, role string, rc *config.Runtime return nil } +// isAutonomousRole returns true if the given role should automatically +// inject mail check on startup. Autonomous roles (polecat, witness, +// refinery, deacon) operate without human prompting and need mail injection +// to receive work assignments. +// +// Non-autonomous roles (mayor, crew) are human-guided and should not +// have automatic mail injection to avoid confusion. func isAutonomousRole(role string) bool { switch role { case "polecat", "witness", "refinery", "deacon": From 947111f6d8127b3f44371e3668b24283ae0b4b1e Mon Sep 17 00:00:00 2001 From: slit Date: Sun, 11 Jan 2026 11:00:45 -0500 Subject: [PATCH 5/6] docs(mq): add parameter godoc for GenerateMRIDWithTime Add detailed parameter documentation for GenerateMRIDWithTime function including prefix, branch, and timestamp parameters with examples. --- internal/mq/id.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/internal/mq/id.go b/internal/mq/id.go index 3ec021d2..9468d511 100644 --- a/internal/mq/id.go +++ b/internal/mq/id.go @@ -30,6 +30,13 @@ func GenerateMRID(prefix, branch string) string { // GenerateMRIDWithTime generates a merge request ID using a specific timestamp. // This is primarily useful for testing to ensure deterministic output. // Note: Without randomness, two calls with identical inputs will produce the same ID. +// +// Parameters: +// - prefix: The project prefix (e.g., "gt" for gastown, "bd" for beads) +// - branch: The source branch name (e.g., "polecat/Nux/gt-xyz") +// - timestamp: The time to use for ID generation instead of time.Now() +// +// Returns a string in the format "-mr-<6-char-hash>" func GenerateMRIDWithTime(prefix, branch string, timestamp time.Time) string { return generateMRIDInternal(prefix, branch, timestamp, nil) } From e16d5840c6ea33add84bf12e3982ddbfcc223b78 Mon Sep 17 00:00:00 2001 From: mayor Date: Sun, 11 Jan 2026 11:29:02 -0500 Subject: [PATCH 6/6] docs: clarify gt prime is context recovery, not session start (GH #308) gt prime recovers context inside an existing session (after compaction, clear, or new session). It's not an alternative to 'gt mayor attach' which starts a new Mayor session. --- README.md | 37 ++++++++----------------------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index da6dbf13..b1c124ec 100644 --- a/README.md +++ b/README.md @@ -77,27 +77,6 @@ Work tracking units. Bundle multiple issues/tasks that get assigned to agents. Git-backed issue tracking system that stores work state as structured data. -### Issue IDs 🔢 - -Gas Town uses [Beads](https://github.com/steveyegge/beads) for issue tracking, which generates **short random IDs** - not sequential numbers like GitHub issues. - -``` -gt-x7k2m ← Gas Town issue (gt prefix) -bd-9np4q ← Beads issue (bd prefix) -myproj-abc3 ← Your project (custom prefix) -``` - -The prefix comes from your project's Beads configuration. Find yours with `bd config get prefix`. - -**Common commands:** -```bash -bd list # List all issues -bd show gt-x7k2m # Show issue details -bd ready # Issues ready for work -``` - -> **Note:** These are Beads IDs, not GitHub issue numbers. GitHub issue #309 might have Beads ID `gt-uzx2c`. - > **New to Gas Town?** See the [Glossary](docs/glossary.md) for a complete guide to terminology and concepts. ## Installation @@ -163,10 +142,10 @@ sequenceDiagram gt mayor attach # 2. In Mayor session, create a convoy -gt convoy create "Feature X" gt-x7k2m gt-9np4q --notify --human +gt convoy create "Feature X" issue-123 issue-456 --notify --human # 3. Assign work to an agent -gt sling gt-x7k2m myproject +gt sling issue-123 myproject # 4. Track progress gt convoy list @@ -198,7 +177,7 @@ flowchart LR gt mayor attach # In Mayor, create convoy and let it orchestrate -gt convoy create "Auth System" gt-a3k7x gt-b2m9y --notify +gt convoy create "Auth System" issue-101 issue-102 --notify # Track progress gt convoy list @@ -209,8 +188,8 @@ gt convoy list Run individual runtime instances manually. Gas Town just tracks state. ```bash -gt convoy create "Fix bugs" gt-f4n8p # Create convoy (sling auto-creates if skipped) -gt sling gt-f4n8p myproject # Assign to worker +gt convoy create "Fix bugs" issue-123 # Create convoy (sling auto-creates if skipped) +gt sling issue-123 myproject # Assign to worker claude --resume # Agent reads mail, runs work (Claude) # or: codex # Start Codex in the workspace gt convoy list # Check progress @@ -285,10 +264,10 @@ bd mol pour release --var version=1.2.0 gt convoy create "Bug Fixes" --human # Add issues -gt convoy add-issue gt-c5p2q gt-d8r3s +gt convoy add-issue bug-101 bug-102 # Assign to specific agents -gt sling gt-c5p2q myproject/my-agent +gt sling bug-101 myproject/my-agent # Check status gt convoy show @@ -337,7 +316,7 @@ gt sling # Assign work to agent gt sling --agent cursor # Override runtime for this sling/spawn gt mayor attach # Start Mayor session gt mayor start --agent auggie # Run Mayor with a specific agent alias -gt prime # Alternative to mayor attach +gt prime # Context recovery (run inside existing session) ``` **Built-in agent presets**: `claude`, `gemini`, `codex`, `cursor`, `auggie`, `amp`