Remove state files from witness and refinery managers, following
the "Discover, Don't Track" principle. Tmux session existence is
now the source of truth for running state (like deacon).
Changes:
- Add IsRunning() that checks tmux HasSession
- Change Status() to return *tmux.SessionInfo
- Remove loadState/saveState/stateManager
- Simplify Start()/Stop() to not use state files
- Update CLI commands (witness/refinery/rig) for new API
- Update tests to be ZFC-compliant
This fixes state file divergence issues where witness/refinery
could show "running" when the actual tmux session was dead.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add initial prompt to deacon and witness startup commands to trigger
autonomous patrol. Without this, agents sit idle at the prompt after
SessionStart hooks run.
Implements GUPP (Gas Town Universal Propulsion Principle): agents start
patrol immediately when Claude launches.
Fixes#525: gt up reports deacon success but session doesn't actually start
Previously, WaitForCommand failures were marked as "non-fatal" in the
manager Start() methods used by gt up. This caused gt up to report
success even when Claude failed to start, because the error was silently
ignored.
Now when WaitForCommand or WaitForRuntimeReady times out:
1. The zombie tmux session is killed
2. An error is returned to the caller
3. gt up properly reports the failure
This aligns the manager Start() behavior with the cmd start functions
(e.g., gt deacon start) which already had fatal WaitForCommand behavior.
Changed files:
- internal/deacon/manager.go
- internal/mayor/manager.go
- internal/witness/manager.go
- internal/refinery/manager.go
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix(sling_test): update test for cook dir change
The cook command no longer needs database context and runs from cwd,
not the target rig directory. Update test to match this behavior
change from bd2a5ab5.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(tests): skip tests requiring missing binaries, handle --allow-stale
- Add skipIfAgentBinaryMissing helper to skip tests when codex/gemini
binaries aren't available in the test environment
- Update rig manager test stub to handle --allow-stale flag
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor(config): remove BEADS_DIR from agent environment
Stop exporting BEADS_DIR in AgentEnv - agents should use beads redirect
mechanism instead of relying on environment variable. This prevents
prefix mismatches when agents operate across different beads databases.
Changes:
- Remove BeadsDir field from AgentEnvConfig
- Remove BEADS_DIR from env vars set on agent sessions
- Update doctor env_check to not expect BEADS_DIR
- Update all manager Start() calls to not pass BeadsDir
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(doctor): detect BEADS_DIR in tmux session environment
Add a doctor check that warns when BEADS_DIR is set in any Gas Town
tmux session. BEADS_DIR in the environment overrides prefix-based
routing and breaks multi-rig lookups - agents should use the beads
redirect mechanism instead.
The check:
- Iterates over all Gas Town tmux sessions (gt-* and hq-*)
- Checks if BEADS_DIR is set in the session environment
- Returns a warning with fix hint to restart sessions
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Migrate witness, boot, and deacon spawns to use NewSessionWithCommand
instead of NewSession+SendKeys to ensure BD_ACTOR is visible in the
process tree for orphan detection via ps.
Refs: gt-emi5b
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Updated comment to use "orphaned polecats" instead of "idle polecats".
With the self-cleaning model, polecats self-nuke on completion.
An orphan is from a crash, not a normal idle state.
Closes: gt-7l8y1
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The witness manager was using rig-level beads path to look up role
configuration, but role beads use the hq- prefix and live in town-level
beads. This caused "unexpected end of JSON input" errors when starting
witnesses because the rig database (with gt- prefix) couldn't find
hq-witness-role.
Changed roleConfig() to use townRoot instead of rig.BeadsPath() to
correctly resolve town-level role beads.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Replace ProcessExists() checks in witness and refinery managers with
tmux session detection. Agent liveness should be derived from tmux
session state, not PID probing (per ZFC tracking principles).
- Remove util.ProcessExists() from witness/manager.go and refinery/manager.go
- Delete internal/util/process.go and process_test.go (now unused)
- Foreground mode and Stop() now rely solely on tmux HasSession/KillSession
Closes: hq-yxkdr (recentDeaths already removed)
Closes: hq-1sd4o (ProcessExists removed)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extends the --agent flag with a more general --env flag that allows
setting arbitrary environment variables when starting a witness.
Precedence (highest to lowest):
1. CLI --env overrides
2. Role bead env_vars
3. config.AgentEnv() defaults
Examples:
gt witness start greenplace --env ANTHROPIC_MODEL=claude-3-haiku
gt witness restart greenplace --env DEBUG=1 --env VERBOSE=true
Co-authored-by: joshuavial <git@codewithjv.com>
Create centralized AgentEnv function as single source of truth for all
agent environment variables. All agents now consistently receive:
- GT_ROLE, BD_ACTOR, GIT_AUTHOR_NAME (role identity)
- GT_ROOT, BEADS_DIR (workspace paths)
- GT_RIG, GT_POLECAT/GT_CREW (rig-specific identity)
- BEADS_AGENT_NAME, BEADS_NO_DAEMON (beads config)
- CLAUDE_CONFIG_DIR (optional account selection)
Remove RoleEnvVars in favor of AgentEnvSimple wrapper.
Remove IncludeBeadsEnv flag - beads env vars always included.
Update all manager and cmd call sites to use AgentEnv.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Consolidates all role startup code to use the shared RoleEnvVars()
function, ensuring consistent env vars across tmux SetEnvironment
and Claude startup command exports.
Updated:
- Mayor manager
- Deacon startup (daemon.go)
- Witness manager
- Refinery manager
- Polecat startup (daemon.go)
- BuildPolecatStartupCommand, BuildCrewStartupCommand helpers
This ensures all agents receive the same identity env vars regardless
of startup path.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Agent sessions would fail on startup because send-keys arrived before the
shell was ready, causing 'bad pattern' and 'command not found' errors.
Fix: Create sessions with the command directly using tmux new-session's
command argument. This runs the agent as the pane's initial process,
avoiding shell readiness timing issues entirely.
Updated all agent managers: mayor, deacon, witness, refinery, polecat, crew.
Also fixes pre-existing build error in polecat/manager.go (polecatPath →
clonePath/newClonePath).
Closes#280
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changes polecat worktree structure from:
polecats/<name>/
to:
polecats/<name>/<rigname>/
This gives Claude Code agents a recognizable directory name (e.g., tidepool/)
in their cwd instead of just the polecat name, preventing confusion about
which repo they are working in.
Key changes:
- Add clonePath() method to manager.go and session_manager.go for the actual
git worktree path, keeping polecatDir() for existence checks
- Update Add(), RepairWorktree(), Remove() to use new structure
- Update daemon lifecycle and restart code for new paths
- Update witness handlers to detect both structures
- Update doctor checks (rig_check, branch_check, config_check,
claude_settings_check) for backward compatibility
- All code includes fallback to old structure for existing polecats
Fixes#283
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add WaitForShellReady call before SendKeys in all agent managers
(deacon, mayor, witness, refinery). This prevents intermittent
"can't find pane" errors that occur when the tmux session is
created but the shell isn't ready to receive input yet.
The issue manifests under load (e.g., during `gt up` when multiple
agents start in sequence) where the 200ms delay in SendKeysDelayed
isn't sufficient for the pane to be fully initialized.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Witness was calling BuildAgentStartupCommand with empty rigPath,
causing it to fall back to town-level defaults instead of honoring
rig-specific agent settings (like RigSettings.Agent).
Now passes m.rig.Path so rig agent settings are honored, consistent
with how refinery already passes the rig path.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create mayor.Manager for mayor lifecycle (Start/Stop/IsRunning/Status)
- Create deacon.Manager for deacon lifecycle with respawn loop
- Move session.Manager to polecat.SessionManager (clearer naming)
- Add zombie session detection for mayor/deacon (kills tmux if Claude dead)
- Remove duplicate session startup code from up.go, start.go, mayor.go
- Rename sessMgr -> polecatMgr for consistency
- Make witness/refinery SessionName() public for status display
All agent types now follow the same Manager pattern:
mgr := agent.NewManager(...)
mgr.Start(...)
mgr.Stop()
mgr.IsRunning()
mgr.Status()
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Refactors all agent startup paths (witness, refinery, crew, polecat) to use
a consistent Manager interface with Start(), Stop(), IsRunning(), and
SessionName() methods.
Includes:
- Witness manager with GUPP propulsion nudge for startup
- Refinery manager for engineer sessions
- Crew manager for worker agents
- Session/polecat manager updates
- claude_settings_check doctor check for settings validation
- Settings management consolidated from rig/manager.go
- Settings location moved outside source repos to prevent conflicts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Resolved conflict in internal/witness/manager.go:
- Kept session import (used by PR code)
- Kept PR's more accurate comment for PID check
- Removed duplicate sessionName method introduced by merge
The witness manager's Stop() method was only updating runtime JSON state
without killing the tmux session, causing 'gt rig shutdown' to leave
witness sessions running.
Added sessionName() method and tmux kill-session logic to match the
refinery's existing implementation.
Fixes: bd-gxaf
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The Witness handlers (HandlePolecatDone, HandleMerged, etc.) existed in Go
code but were never called - there was no CLI command to invoke them.
This caused polecats to remain in 'done' state after MR merge because
POLECAT_DONE messages were never processed.
Changes:
- Add `gt witness process <rig>` command to process Witness mail
- Fix --wisp flag to --ephemeral in cleanup wisp creation
- Command processes POLECAT_DONE, MERGED, HELP, SWARM_START messages
- Auto-nukes clean polecats, creates cleanup wisps for dirty ones
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 4 of local-only polecat branches: Handle conflict resolution edge case.
Problem: If polecat worktree is nuked before MR merges, the local branch
is gone and conflict resolution can't access it.
Solution: Witness now defers cleanup for polecats with pending MRs:
- HandlePolecatDone creates a cleanup wisp with "merge-requested" state
- Polecat worktree preserved until MERGED signal arrives
- HandleMerged then nukes the polecat (existing behavior)
Also updated mol-polecat-conflict-resolve.formula.toml:
- Removed fetch from origin (branches are local-only now)
- Added instructions to fetch from source polecat's worktree
- Added rig and source_polecat variables
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When the Refinery detects a build error or test failure and refuses
to merge, the polecat was never notified. This fixes the notification
pipeline by:
1. Adding MERGE_FAILED protocol support to Witness:
- PatternMergeFailed regex pattern
- ProtoMergeFailed protocol type constant
- MergeFailedPayload struct with all failure details
- ParseMergeFailed parser function
- ClassifyMessage case for MERGE_FAILED
2. Adding HandleMergeFailed handler to Witness:
- Parses the failure notification
- Sends HIGH priority mail to polecat with fix instructions
- Includes branch, issue, failure type, and error details
3. Adding mail notification in Refinery's handleFailureFromQueue:
- Creates mail.Router for sending protocol messages
- Sends MERGE_FAILED to Witness when merge fails
- Includes failure type (build/tests/conflict) and error
4. Adding comprehensive unit tests:
- TestParseMergeFailed for full body parsing
- TestParseMergeFailed_MinimalBody for minimal body
- TestParseMergeFailed_InvalidSubject for error handling
- ClassifyMessage test cases for MERGE_FAILED
Fixes#114🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Witness and Refinery startup was duplicated across cmd/witness.go, cmd/up.go,
cmd/rig.go, and daemon.go. Worse, not all code paths sent the propulsion nudge
(GUPP - Gas Town Universal Propulsion Principle). Now unified in Manager.Start()
which handles everything including nudges.
Changes:
- witness/manager.go: Full rewrite with session creation, env vars, theming,
WaitForClaudeReady, startup nudge, and propulsion nudge (GUPP)
- refinery/manager.go: Add propulsion nudge sequence after Claude startup
- cmd/witness.go: Simplify to just call mgr.Start(), remove ensureWitnessSession
- cmd/rig.go: Use witness.Manager.Start() instead of inline session creation
- cmd/start.go: Use witness.Manager.Start()
- cmd/up.go: Use witness.Manager.Start(), remove ensureWitness(),
add EnsureSettingsForRole in ensureSession()
- daemon.go: Use witness.Manager.Start() and refinery.Manager.Start() for
unified startup with proper nudges
This ensures all agent startup paths (gt witness start, gt rig boot, gt up,
daemon restarts) consistently apply GUPP propulsion nudges.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create internal/agent package with shared State type and StateManager
- StateManager uses Go generics for type-safe load/save operations
- Update witness and refinery to use shared State type alias
- Replace loadState/saveState implementations with StateManager delegation
- Maintains backwards compatibility through re-exported constants
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Introduces a typed CleanupStatus with constants:
- CleanupClean, CleanupUncommitted, CleanupStash, CleanupUnpushed, CleanupUnknown
Adds helper methods:
- IsSafe(): true for clean status
- RequiresRecovery(): true for uncommitted/stash/unpushed
- CanForceRemove(): true if force flag can bypass
Updated files to use the new type:
- internal/polecat/types.go: Type definition and methods
- internal/polecat/manager.go: Validation logic
- internal/witness/handlers.go: Nuke safety checks
- internal/cmd/done.go: Status reporting
- internal/cmd/polecat.go: Recovery status checks
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create util.ExecWithOutput and util.ExecRun to consolidate repeated
exec.Command patterns across witness/handlers.go and refinery/manager.go.
Changes:
- Add internal/util/exec.go with ExecWithOutput (returns stdout) and
ExecRun (runs command without output)
- Refactor witness/handlers.go to use utility functions (7 call sites)
- Refactor refinery/manager.go, removing unused gitRun/gitOutput methods
- Add comprehensive tests in exec_test.go
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
In multi-remote setups, the code may live on a remote other than
"origin" (e.g., "gastown" for gastown.git). The verifyCommitOnMain
function now iterates through all configured remotes to find the
one containing the default branch with the merged commit.
Changes:
- Add git.Remotes() method to list all configured remote names
- Update verifyCommitOnMain to check all remotes/<defaultBranch>
instead of only origin/<defaultBranch>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add --branch flag to `gt rig add` to specify a custom default branch
instead of auto-detecting from remote. This supports repositories that
use non-standard default branches like `develop` or `release`.
Changes:
- Add --branch flag to `gt rig add` command
- Store default_branch in rig config.json
- Propagate default branch to refinery, witness, daemon, and all commands
- Rename ensureMainBranch to ensureDefaultBranch for clarity
- Add Rig.DefaultBranch() method for consistent access
- Update crew/manager.go and swarm/manager.go to use rig config
Based on PR #49 by @kustrun - rebased and extended with additional fixes.
Co-authored-by: kustrun <kustrun@users.noreply.github.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The Witness lifecycle was not reliably killing polecat tmux sessions after
POLECAT_DONE. Sessions were piling up because gt polecat nuke may fail to
kill the session due to rig loading issues or race conditions with
IsRunning checks.
Fix: Kill the tmux session FIRST and unconditionally in NukePolecat,
before calling gt polecat nuke. This ensures the session is always killed
regardless of what happens in the downstream nuke command.
The session name pattern is deterministic (gt-<rig>-<polecat>), so we can
construct it directly from the rigName and polecatName parameters.
Fixes: gt-g9ft5
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Updates HandlePolecatDone to auto-nuke polecats immediately after MR submission
when cleanup_status=clean. This separates polecat lifecycle from MR lifecycle:
- Polecat lifecycle: spawning → working → mr_submitted → nuked
- MR lifecycle: created → queued → processed → merged (handled by Refinery)
Key changes:
- Try auto-nuke for ALL POLECAT_DONE messages regardless of MR status
- If cleanup_status=clean (branch pushed), nuke immediately
- If dirty state, create cleanup wisp for manual intervention
- Cleanup wisps are now exception handling, not the normal flow
Conflict resolution is handled by the Refinery, which creates NEW tasks
for NEW polecats when merge conflicts are detected.
(gt-si8rq.9)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add support for signaling phase completion when a polecat needs to wait
on a gate before continuing. The --phase-complete flag with --gate ID
allows polecats to hand off control while awaiting external conditions.
Changes:
- done.go: Add --phase-complete and --gate flags, PHASE_COMPLETE exit type
- protocol.go: Add Gate field to PolecatDonePayload
- handlers.go: Handle PHASE_COMPLETE by recycling session (keep worktree)
- beads.go: Add AddGateWaiter method for gate registration
This enables multi-phase molecule workflows with async coordination (bd-gxb4)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements automatic nuke of polecats after completion:
- HandlePolecatDone: Auto-nukes immediately for ESCALATED/DEFERRED exits
(no pending MR) when cleanup_status=clean
- HandleMerged: Auto-nukes after verifying commit is on main and
cleanup_status=clean
- HandleLifecycleShutdown: Auto-nukes immediately since shutdown means
no pending work
Safety checks preserved:
- Verifies cleanup_status from agent bead (clean/has_uncommitted/etc)
- Verifies commit is on main before nuking
- Falls back to cleanup wisp if any check fails
New functions:
- NukePolecat(): Executes gt polecat nuke
- AutoNukeIfClean(): Checks safety and nukes if safe
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The getCleanupStatus function in witness/handlers.go and crew removal
in cmd/crew_lifecycle.go were constructing agent bead IDs with hardcoded
"gt-" prefix. This failed for rigs that use a different prefix like "bd-".
Now uses beads.GetPrefixForRig to look up the correct prefix from
routes.jsonl, enabling support for the beads rig and any future rigs
with custom prefixes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add verifyCommitOnMain() check in HandleMerged to ensure the polecat's
commit is actually on main before allowing cleanup. This prevents work
loss when:
- MERGED signal is for a stale MR
- Merge attempt failed after signal was sent
- MR was already merged via different path
The verification uses git merge-base --is-ancestor to confirm the
polecat's HEAD is reachable from main.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add safety check to HandleMerged that verifies cleanup_status from the
polecat's agent bead before allowing nuke. This prevents work loss when:
- MERGED signal arrives for a stale MR (already merged via different path)
- Polecat has new unpushed work since the MR was created
- Race condition between merge and nuke
The Witness now blocks nuke and sets an error for escalation when:
- has_uncommitted: polecat has uncommitted changes
- has_stash: polecat has stashed work
- has_unpushed: polecat has unpushed commits (critical)
Clean status or unknown status (backward compat) allows nuke to proceed.
(gt-5q6jr)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add gt polecat check-recovery command to check cleanup_status from agent bead
- Update gt polecat nuke to block on polecats with unpushed work (use --force to override)
- Add EscalateRecoveryNeeded function for RECOVERY_NEEDED escalations
- Update Witness template with dormant polecat recovery protocol
Prevents accidental data loss when cleaning up dormant polecats that have
unpushed commits. The Witness should now use check-recovery before nuking
and escalate NEEDS_RECOVERY cases to the Mayor.
(gt-cloml)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add protocol message parsing and handlers for polecat→witness communication:
- POLECAT_DONE: Parse completion messages, create cleanup wisps
- HELP: Parse help requests, assess if Witness can help or escalate to Mayor
- MERGED: Parse refinery merge confirmations
- LIFECYCLE:Shutdown: Handle daemon-triggered shutdowns
- SWARM_START: Parse batch work initialization
Files added:
- internal/witness/protocol.go: Message classification and parsing
- internal/witness/handlers.go: Handler implementations with wisp/mail integration
- internal/witness/protocol_test.go: Unit tests for all parsing functions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove Stats fields from witness and refinery types that tracked
observable metrics (total counts, today counts). These are now
handled by the activity stream/beads system instead.
Removed:
- WitnessStats type and Stats field from Witness
- RefineryStats type and Stats field from Refinery
- LastCheckAt field from Witness
- Stats display from gt witness status
- Stats display from gt refinery status
- Stats increment code from refinery.completeMR()
Kept minimal process state:
- RigName, State, PID, StartedAt (both)
- MonitoredPolecats, Config, SpawnedIssues (witness)
- CurrentMR, PendingMRs, LastMergeAt (refinery)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move duplicated processExists function to shared util package:
- Create internal/util/process.go with ProcessExists function
- Add internal/util/process_test.go with basic tests
- Update witness/manager.go to use util.ProcessExists
- Update refinery/manager.go to use util.ProcessExists
- Remove local processExists functions from both files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Prevents data loss from concurrent/interrupted state file writes by using
atomic write pattern (write to .tmp, then rename).
Changes:
- Add internal/util package with AtomicWriteJSON/AtomicWriteFile helpers
- Update witness/manager.go saveState to use atomic writes
- Update refinery/manager.go saveState to use atomic writes
- Update crew/manager.go saveState to use atomic writes
- Update daemon/types.go SaveState to use atomic writes
- Update polecat/namepool.go Save to use atomic writes
- Add comprehensive tests for atomic write utilities
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
All 156 instances of _ = error suppression in non-test code now have
explanatory comments documenting why the error is intentionally ignored.
Categories of intentional suppressions:
- non-fatal: session works without these - tmux environment setup
- non-fatal: theming failure does not affect operation - visual styling
- best-effort cleanup - defer cleanup on failure paths
- best-effort notification - mail/notifications that should not block
- best-effort interrupt - graceful shutdown attempts
- crypto/rand.Read only fails on broken system - random ID generation
- output errors non-actionable - fmt.Fprint to io.Writer
This addresses the silent failure and debugging concerns raised in the
issue by making the intentionality explicit in the code.
Generated with Claude Code https://claude.com/claude-code
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>