feat(checkpoint): Add polecat session checkpoint for crash recovery (gt-441j6)
Add checkpoint system for polecats and crew workers to recover state after session crash or context limit. Features: - internal/checkpoint package with Checkpoint type - gt checkpoint write/read/clear commands - Checkpoint display in gt prime startup - Auto-detection of molecule, step, hooked bead - Git state capture (modified files, branch, commit) The checkpoint captures: - Current molecule and step being worked - Hooked bead - Modified files list - Git branch and last commit - Session notes - Timestamp Checkpoints are stored in .polecat-checkpoint.json and displayed during session startup via gt prime. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,9 +9,11 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/steveyegge/gastown/internal/beads"
|
||||
"github.com/steveyegge/gastown/internal/checkpoint"
|
||||
"github.com/steveyegge/gastown/internal/constants"
|
||||
"github.com/steveyegge/gastown/internal/events"
|
||||
"github.com/steveyegge/gastown/internal/lock"
|
||||
@@ -134,6 +136,9 @@ func runPrime(cmd *cobra.Command, args []string) error {
|
||||
// Output molecule context if working on a molecule step
|
||||
outputMoleculeContext(ctx)
|
||||
|
||||
// Output previous session checkpoint for crash recovery
|
||||
outputCheckpointContext(ctx)
|
||||
|
||||
// Run bd prime to output beads workflow context
|
||||
runBdPrime(cwd)
|
||||
|
||||
@@ -1418,6 +1423,75 @@ func checkPendingEscalations(ctx RoleContext) {
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// outputCheckpointContext reads and displays any previous session checkpoint.
|
||||
// This enables crash recovery by showing what the previous session was working on.
|
||||
func outputCheckpointContext(ctx RoleContext) {
|
||||
// Only applies to polecats and crew workers
|
||||
if ctx.Role != RolePolecat && ctx.Role != RoleCrew {
|
||||
return
|
||||
}
|
||||
|
||||
// Read checkpoint
|
||||
cp, err := checkpoint.Read(ctx.WorkDir)
|
||||
if err != nil {
|
||||
// Silently ignore read errors
|
||||
return
|
||||
}
|
||||
if cp == nil {
|
||||
// No checkpoint exists
|
||||
return
|
||||
}
|
||||
|
||||
// Check if checkpoint is stale (older than 24 hours)
|
||||
if cp.IsStale(24 * time.Hour) {
|
||||
// Remove stale checkpoint
|
||||
_ = checkpoint.Remove(ctx.WorkDir)
|
||||
return
|
||||
}
|
||||
|
||||
// Display checkpoint context
|
||||
fmt.Println()
|
||||
fmt.Printf("%s\n\n", style.Bold.Render("## 📌 Previous Session Checkpoint"))
|
||||
fmt.Printf("A previous session left a checkpoint %s ago.\n\n", cp.Age().Round(time.Minute))
|
||||
|
||||
if cp.StepTitle != "" {
|
||||
fmt.Printf(" **Working on:** %s\n", cp.StepTitle)
|
||||
}
|
||||
if cp.MoleculeID != "" {
|
||||
fmt.Printf(" **Molecule:** %s\n", cp.MoleculeID)
|
||||
}
|
||||
if cp.CurrentStep != "" {
|
||||
fmt.Printf(" **Step:** %s\n", cp.CurrentStep)
|
||||
}
|
||||
if cp.HookedBead != "" {
|
||||
fmt.Printf(" **Hooked bead:** %s\n", cp.HookedBead)
|
||||
}
|
||||
if cp.Branch != "" {
|
||||
fmt.Printf(" **Branch:** %s\n", cp.Branch)
|
||||
}
|
||||
if len(cp.ModifiedFiles) > 0 {
|
||||
fmt.Printf(" **Modified files:** %d\n", len(cp.ModifiedFiles))
|
||||
// Show first few files
|
||||
maxShow := 5
|
||||
if len(cp.ModifiedFiles) < maxShow {
|
||||
maxShow = len(cp.ModifiedFiles)
|
||||
}
|
||||
for i := 0; i < maxShow; i++ {
|
||||
fmt.Printf(" - %s\n", cp.ModifiedFiles[i])
|
||||
}
|
||||
if len(cp.ModifiedFiles) > maxShow {
|
||||
fmt.Printf(" ... and %d more\n", len(cp.ModifiedFiles)-maxShow)
|
||||
}
|
||||
}
|
||||
if cp.Notes != "" {
|
||||
fmt.Printf(" **Notes:** %s\n", cp.Notes)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
fmt.Println("Use this context to resume work. The checkpoint will be updated as you progress.")
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// emitSessionEvent emits a session_start event for seance discovery.
|
||||
// The event is written to ~/gt/.events.jsonl and can be queried via gt seance.
|
||||
// Session ID comes from CLAUDE_SESSION_ID env var if available.
|
||||
|
||||
Reference in New Issue
Block a user