Deacon uses wisp-based patrol (gt-3x0z.9)
Daemon changes: - Remove checkDeaconAttachment() - Deacon self-spawns wisps - Remove findDeaconPatrolMolecule() - unused - Remove nudgeDeaconForPatrol() - unused - Remove DeaconPatrolMolecule const - unused - Remove beads import - no longer needed Deacon template changes: - Update to wisp-based patrol model - Replace bd mol run with bd mol spawn (wisps by default) - Remove pinned molecule concept for patrol - Add Why Wisps section explaining ephemeral design - Update startup/handoff protocols for wisp-based cycles 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -37,8 +37,8 @@ Work in Gas Town exists in three phases, following the states of matter:
|
||||
|
||||
### Proto (Solid Phase)
|
||||
|
||||
Protos are **frozen workflow patterns** - crystallized templates that encode
|
||||
reusable work structures. They're the "molds" from which instances are cast.
|
||||
Protos or protomolecules are **frozen workflow patterns** - crystallized templates that
|
||||
encode reusable work structures. They're the "molds" from which instances are cast.
|
||||
|
||||
```markdown
|
||||
## Molecule: engineer-in-box
|
||||
@@ -52,9 +52,13 @@ Needs: (none)
|
||||
Write the code.
|
||||
Needs: design
|
||||
|
||||
## Step: review
|
||||
Perform initial code review.
|
||||
Needs: implement
|
||||
|
||||
## Step: test
|
||||
Write and run tests.
|
||||
Needs: implement
|
||||
Needs: review
|
||||
|
||||
## Step: submit
|
||||
Submit for merge.
|
||||
@@ -62,10 +66,11 @@ Needs: test
|
||||
```
|
||||
|
||||
**Properties:**
|
||||
- Immutable once defined (frozen)
|
||||
- Named (e.g., `mol-engineer-in-box`, `mol-code-review`)
|
||||
- Considered immutable once defined (frozen), though editable
|
||||
- Named (e.g., `mol-engineer-in-box`, `wisp-deacon-patrol`)
|
||||
- Can have any name, but convention says how they will materialize.
|
||||
- Stored in permanent beads with `template` label
|
||||
- Can be composed into larger protos (polymers)
|
||||
- Can be composed into larger protos (compounds, polymers)
|
||||
|
||||
### Mol (Liquid Phase)
|
||||
|
||||
|
||||
@@ -460,10 +460,13 @@ func outputStartupDirective(ctx RoleContext) {
|
||||
fmt.Println("**STARTUP PROTOCOL**: You are the Deacon. Please:")
|
||||
fmt.Println("1. Announce: \"Deacon, checking in.\"")
|
||||
fmt.Println("2. Signal awake: `gt deacon heartbeat \"starting patrol\"`")
|
||||
fmt.Println("3. Check for attached patrol: `bd list --status=in_progress --assignee=deacon`")
|
||||
fmt.Println("4. If attached: resume from current step")
|
||||
fmt.Println("5. If naked: `gt mol bond mol-deacon-patrol`")
|
||||
fmt.Println("3. cd to rig: `cd gastown/mayor/rig`")
|
||||
fmt.Println("4. Check Patrol Status above - if attached, resume from current step")
|
||||
fmt.Println("5. If naked, start wisp patrol:")
|
||||
fmt.Println(" - Find proto ID: `bd mol list` (look for mol-deacon-patrol)")
|
||||
fmt.Println(" - Spawn: `bd --no-daemon mol spawn <proto-id>`")
|
||||
fmt.Println("6. Execute patrol steps until loop-or-exit")
|
||||
fmt.Println("7. At cycle end: `bd --no-daemon mol squash <mol-id> --summary \"<cycle summary>\"`")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,70 +612,86 @@ func showMoleculeProgress(b *beads.Beads, rootID string) {
|
||||
}
|
||||
|
||||
// outputDeaconPatrolContext shows patrol molecule status for the Deacon.
|
||||
// Deacon uses wisps (Wisp:true issues in main .beads/) for patrol cycles.
|
||||
// Spawn creates wisp-marked issues that are auto-deleted on squash.
|
||||
func outputDeaconPatrolContext(ctx RoleContext) {
|
||||
b := beads.New(ctx.TownRoot)
|
||||
|
||||
// Check for in-progress patrol steps assigned to deacon
|
||||
issues, err := b.List(beads.ListOptions{
|
||||
Status: "in_progress",
|
||||
Assignee: "deacon",
|
||||
Priority: -1,
|
||||
})
|
||||
if err != nil {
|
||||
// Silently skip if beads lookup fails
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("%s\n\n", style.Bold.Render("## 🔄 Patrol Status"))
|
||||
fmt.Printf("%s\n\n", style.Bold.Render("## 🔄 Patrol Status (Wisp-based)"))
|
||||
|
||||
if len(issues) == 0 {
|
||||
// No attached molecule - show "naked" status
|
||||
// Check for active mol-deacon-patrol molecules in rig beads
|
||||
// A patrol is "active" if it has open wisp children (steps to execute)
|
||||
// After squash, the root stays open but has no open children - that's "completed"
|
||||
rigBeadsDir := filepath.Join(ctx.TownRoot, "gastown", "mayor", "rig")
|
||||
|
||||
// First find mol-deacon-patrol molecules (exclude template)
|
||||
cmdList := exec.Command("bd", "list", "--status=open", "--type=epic")
|
||||
cmdList.Dir = rigBeadsDir
|
||||
var stdoutList bytes.Buffer
|
||||
cmdList.Stdout = &stdoutList
|
||||
cmdList.Stderr = nil
|
||||
errList := cmdList.Run()
|
||||
|
||||
// Find a patrol molecule with open children
|
||||
hasPatrol := false
|
||||
var patrolLine string
|
||||
var patrolID string
|
||||
if errList == nil {
|
||||
lines := strings.Split(stdoutList.String(), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "mol-deacon-patrol") && !strings.Contains(line, "[template]") {
|
||||
// Extract the ID (first word)
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) > 0 {
|
||||
molID := parts[0]
|
||||
// Check if this molecule has open children using bd show
|
||||
cmdShow := exec.Command("bd", "show", molID)
|
||||
cmdShow.Dir = rigBeadsDir
|
||||
var stdoutShow bytes.Buffer
|
||||
cmdShow.Stdout = &stdoutShow
|
||||
cmdShow.Stderr = nil
|
||||
if cmdShow.Run() == nil {
|
||||
showOutput := stdoutShow.String()
|
||||
// Check for "- open]" in children section (open child steps)
|
||||
if strings.Contains(showOutput, "- open]") {
|
||||
hasPatrol = true
|
||||
patrolLine = line
|
||||
patrolID = molID
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = patrolID // Silence unused warning
|
||||
|
||||
if !hasPatrol {
|
||||
// No attached patrol - show "naked" status
|
||||
fmt.Println("Status: **Naked** (no patrol molecule attached)")
|
||||
fmt.Println()
|
||||
fmt.Println("To start patrol:")
|
||||
fmt.Println(" gt mol bond mol-deacon-patrol")
|
||||
fmt.Println("To start patrol cycle:")
|
||||
fmt.Println(" cd gastown/mayor/rig")
|
||||
fmt.Println(" bd mol list # Find mol-deacon-patrol proto ID")
|
||||
fmt.Println(" bd --no-daemon mol spawn <id> # e.g., bd --no-daemon mol spawn gt-iep9")
|
||||
return
|
||||
}
|
||||
|
||||
// Find the patrol molecule step we're working on
|
||||
for _, issue := range issues {
|
||||
// Check if this is a patrol molecule step
|
||||
moleculeID := parseMoleculeMetadata(issue.Description)
|
||||
if moleculeID == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the parent (root) issue ID
|
||||
rootID := issue.Parent
|
||||
if rootID == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// This is a molecule step - show context
|
||||
fmt.Println("Status: **Attached** (patrol molecule in progress)")
|
||||
fmt.Printf(" Current step: %s\n", issue.ID)
|
||||
fmt.Printf(" Molecule: %s\n", moleculeID)
|
||||
fmt.Printf(" Root issue: %s\n\n", rootID)
|
||||
|
||||
// Show patrol progress
|
||||
showMoleculeProgress(b, rootID)
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println("**Patrol Work Loop:**")
|
||||
fmt.Println("1. Execute current step: " + issue.Title)
|
||||
fmt.Println("2. Close step: `bd close " + issue.ID + "`")
|
||||
fmt.Println("3. Check next: `bd ready --parent " + rootID + "`")
|
||||
fmt.Println("4. On final step (loop-or-exit): burn and loop or exit")
|
||||
return
|
||||
}
|
||||
|
||||
// Has issues but none are molecule steps - might be orphaned work
|
||||
fmt.Println("Status: **In-progress work** (not a patrol molecule)")
|
||||
// Has patrol - show attached status
|
||||
fmt.Println("Status: **Attached** (wisp patrol in progress)")
|
||||
fmt.Println()
|
||||
fmt.Println("To start fresh patrol:")
|
||||
fmt.Println(" bd close <in-progress-issues>")
|
||||
fmt.Println(" gt mol bond mol-deacon-patrol")
|
||||
// Show the patrol molecule details
|
||||
fmt.Printf("Active patrol: %s\n\n", strings.TrimSpace(patrolLine))
|
||||
|
||||
fmt.Println("**Wisp Patrol Work Loop:**")
|
||||
fmt.Println("Run from gastown/mayor/rig/:")
|
||||
fmt.Println("1. Check next step: `bd ready`")
|
||||
fmt.Println("2. Execute the step (heartbeat, mail, health checks, etc.)")
|
||||
fmt.Println("3. Close step: `bd close <step-id>`")
|
||||
fmt.Println("4. Check next: `bd ready`")
|
||||
fmt.Println("5. At cycle end (loop-or-exit step):")
|
||||
fmt.Println(" - Generate summary of patrol cycle")
|
||||
fmt.Println(" - Squash: `bd --no-daemon mol squash <mol-id> --summary \"<summary>\"`")
|
||||
fmt.Println(" - Loop back to spawn new wisp, or exit if context high")
|
||||
}
|
||||
|
||||
// acquireIdentityLock checks and acquires the identity lock for worker roles.
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/steveyegge/gastown/internal/beads"
|
||||
"github.com/steveyegge/gastown/internal/config"
|
||||
"github.com/steveyegge/gastown/internal/constants"
|
||||
"github.com/steveyegge/gastown/internal/git"
|
||||
@@ -131,13 +130,11 @@ func (d *Daemon) heartbeat(state *State) {
|
||||
// 1. Ensure Deacon is running (the Deacon is the heartbeat of the system)
|
||||
d.ensureDeaconRunning()
|
||||
|
||||
// 2. Check Deacon attachment - spawn patrol if naked
|
||||
d.checkDeaconAttachment()
|
||||
|
||||
// 3. Poke Deacon - the Deacon monitors Mayor and Witnesses
|
||||
// 2. Poke Deacon - the Deacon monitors Mayor and Witnesses
|
||||
// Note: Deacon self-spawns wisps for patrol cycles (no daemon attachment needed)
|
||||
d.pokeDeacon()
|
||||
|
||||
// 4. Process lifecycle requests
|
||||
// 3. Process lifecycle requests
|
||||
d.processLifecycleRequests()
|
||||
|
||||
// Update state
|
||||
@@ -156,9 +153,6 @@ const DeaconSessionName = "gt-deacon"
|
||||
// DeaconRole is the role name for the Deacon's handoff bead.
|
||||
const DeaconRole = "deacon"
|
||||
|
||||
// DeaconPatrolMolecule is the well-known ID for the deacon patrol molecule.
|
||||
const DeaconPatrolMolecule = "mol-deacon-patrol"
|
||||
|
||||
// deaconMOTDMessages contains rotating motivational and educational tips
|
||||
// for the Deacon heartbeat. These make the thankless patrol role more fun.
|
||||
var deaconMOTDMessages = []string{
|
||||
@@ -227,118 +221,6 @@ func (d *Daemon) ensureDeaconRunning() {
|
||||
d.logger.Println("Deacon session started successfully")
|
||||
}
|
||||
|
||||
// checkDeaconAttachment checks if the Deacon's pinned bead has an attached molecule.
|
||||
// If the Deacon is "naked" (no attachment), this spawns mol-deacon-patrol and
|
||||
// attaches it to drive the Deacon's patrol loop.
|
||||
func (d *Daemon) checkDeaconAttachment() {
|
||||
// Get beads client for the town root
|
||||
bd := beads.New(d.config.TownRoot)
|
||||
|
||||
// Find or create the Deacon's handoff bead
|
||||
pinnedBead, err := bd.GetOrCreateHandoffBead(DeaconRole)
|
||||
if err != nil {
|
||||
d.logger.Printf("Error getting Deacon handoff bead: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the Deacon is naked (no attached molecule)
|
||||
attachment := beads.ParseAttachmentFields(pinnedBead)
|
||||
if attachment != nil && attachment.AttachedMolecule != "" {
|
||||
d.logger.Printf("Deacon has attachment: %s", attachment.AttachedMolecule)
|
||||
return
|
||||
}
|
||||
|
||||
// Deacon is naked - need to attach mol-deacon-patrol
|
||||
d.logger.Println("Deacon is naked (no attached molecule), spawning patrol...")
|
||||
|
||||
// Find the mol-deacon-patrol template
|
||||
molTemplate, err := d.findDeaconPatrolMolecule(bd)
|
||||
if err != nil {
|
||||
d.logger.Printf("Error finding deacon patrol molecule: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Instantiate the patrol molecule with the Deacon's pinned bead as parent
|
||||
steps, err := bd.InstantiateMolecule(molTemplate, pinnedBead, beads.InstantiateOptions{})
|
||||
if err != nil {
|
||||
d.logger.Printf("Error instantiating deacon patrol: %v", err)
|
||||
return
|
||||
}
|
||||
d.logger.Printf("Instantiated mol-deacon-patrol with %d steps", len(steps))
|
||||
|
||||
// Attach the molecule to the Deacon's pinned bead
|
||||
if _, err := bd.AttachMolecule(pinnedBead.ID, molTemplate.ID); err != nil {
|
||||
d.logger.Printf("Error attaching molecule: %v", err)
|
||||
return
|
||||
}
|
||||
d.logger.Printf("Attached %s to Deacon's pinned bead", molTemplate.ID)
|
||||
|
||||
// Nudge the Deacon to start the patrol
|
||||
d.nudgeDeaconForPatrol()
|
||||
}
|
||||
|
||||
// findDeaconPatrolMolecule finds the mol-deacon-patrol template issue.
|
||||
// It first looks for an existing molecule, then falls back to creating from builtin.
|
||||
func (d *Daemon) findDeaconPatrolMolecule(bd *beads.Beads) (*beads.Issue, error) {
|
||||
// Look for existing molecule by title
|
||||
molecules, err := bd.List(beads.ListOptions{Type: "molecule", Priority: -1})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing molecules: %w", err)
|
||||
}
|
||||
|
||||
// Find "Deacon Patrol" molecule
|
||||
for _, mol := range molecules {
|
||||
if mol.Title == "Deacon Patrol" {
|
||||
return mol, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Not found - seed builtin molecules and try again
|
||||
d.logger.Println("Deacon Patrol molecule not found, seeding builtins...")
|
||||
created, err := bd.SeedBuiltinMolecules()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("seeding builtin molecules: %w", err)
|
||||
}
|
||||
d.logger.Printf("Seeded %d builtin molecules", created)
|
||||
|
||||
// Try again
|
||||
molecules, err = bd.List(beads.ListOptions{Type: "molecule", Priority: -1})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing molecules after seed: %w", err)
|
||||
}
|
||||
|
||||
for _, mol := range molecules {
|
||||
if mol.Title == "Deacon Patrol" {
|
||||
return mol, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Deacon Patrol molecule not found after seeding")
|
||||
}
|
||||
|
||||
// nudgeDeaconForPatrol sends a message to the Deacon to start its patrol loop.
|
||||
func (d *Daemon) nudgeDeaconForPatrol() {
|
||||
running, err := d.tmux.HasSession(DeaconSessionName)
|
||||
if err != nil {
|
||||
d.logger.Printf("Error checking Deacon session for nudge: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !running {
|
||||
d.logger.Println("Deacon session not running, cannot nudge")
|
||||
return
|
||||
}
|
||||
|
||||
// Send patrol start message
|
||||
msg := "PATROL: mol-deacon-patrol attached. Start your patrol loop."
|
||||
if err := d.tmux.SendKeysReplace(DeaconSessionName, msg, 50); err != nil {
|
||||
d.logger.Printf("Error nudging Deacon for patrol: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
d.logger.Println("Nudged Deacon to start patrol")
|
||||
}
|
||||
|
||||
// pokeDeacon sends a heartbeat message to the Deacon session.
|
||||
// The Deacon is responsible for monitoring Mayor and Witnesses.
|
||||
func (d *Daemon) pokeDeacon() {
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
## Your Role: DEACON (Patrol Executor)
|
||||
|
||||
You are the **Deacon** - the patrol executor for Gas Town. You execute the
|
||||
`mol-deacon-patrol` molecule in a loop, monitoring agents and handling lifecycle events.
|
||||
`mol-deacon-patrol` molecule as ephemeral wisps in a loop, monitoring agents
|
||||
and handling lifecycle events.
|
||||
|
||||
## Architecture
|
||||
|
||||
@@ -13,15 +14,16 @@ You are the **Deacon** - the patrol executor for Gas Town. You execute the
|
||||
Go Daemon (watches you, auto-starts you if down)
|
||||
|
|
||||
v
|
||||
DEACON (you) ←── Executes mol-deacon-patrol in a loop
|
||||
DEACON (you) ←── Spawns wisps for each patrol cycle
|
||||
|
|
||||
+----+----+
|
||||
v v
|
||||
Mayor Witnesses --> Polecats
|
||||
```
|
||||
|
||||
**Key insight**: You are an AI agent executing a molecule workflow. The molecule
|
||||
defines your patrol steps. You execute each step, close it when done, and loop.
|
||||
**Key insight**: You are an AI agent executing a wisp-based patrol loop. Each
|
||||
patrol cycle is an ephemeral wisp that gets squashed to a digest when complete.
|
||||
This keeps beads clean while maintaining an audit trail.
|
||||
|
||||
## Patrol Molecule: mol-deacon-patrol
|
||||
|
||||
@@ -33,7 +35,7 @@ Your work is defined by the `mol-deacon-patrol` molecule with these steps:
|
||||
4. **orphan-check** - Find abandoned work
|
||||
5. **session-gc** - Clean dead sessions
|
||||
6. **context-check** - Check own context limit
|
||||
7. **loop-or-exit** - Burn and loop, or exit if context high
|
||||
7. **loop-or-exit** - Squash wisp and loop, or exit if context high
|
||||
|
||||
## Wake Sources
|
||||
|
||||
@@ -43,24 +45,20 @@ You wake up when:
|
||||
3. **Timer callback** - Agent scheduled a future wake
|
||||
4. **Startup** - Fresh session or respawn after exit
|
||||
|
||||
## Patrol Execution Protocol
|
||||
## Patrol Execution Protocol (Wisp-Based)
|
||||
|
||||
When you wake, follow this protocol:
|
||||
Each patrol cycle uses an ephemeral wisp:
|
||||
|
||||
### 1. Find Your Current Work
|
||||
### 1. Spawn a Wisp for This Cycle
|
||||
```bash
|
||||
bd list --pinned --assignee=deacon --status=in_progress
|
||||
# Spawn wisp (default for bd mol spawn)
|
||||
bd mol spawn mol-deacon-patrol --assignee=deacon
|
||||
```
|
||||
|
||||
If you have a pinned molecule, **resume from the current step**.
|
||||
If no molecule (naked), **start a new patrol**:
|
||||
```bash
|
||||
bd mol run mol-deacon-patrol
|
||||
```
|
||||
This creates an ephemeral patrol instance. Note: wisps don't pin because they're
|
||||
short-lived and idempotent.
|
||||
|
||||
This spawns the patrol molecule, assigns it to you, pins it, and sets status to in_progress.
|
||||
|
||||
### 2. Execute Current Step
|
||||
### 2. Execute Each Step
|
||||
|
||||
The `mol-deacon-patrol` steps are:
|
||||
|
||||
@@ -93,33 +91,41 @@ gt gc --sessions
|
||||
**context-check**: Check own context limit (self-assess)
|
||||
|
||||
**loop-or-exit**: Decision point
|
||||
- If context LOW: burn molecule, bond new one, repeat
|
||||
- If context HIGH: burn molecule, exit (daemon respawns you)
|
||||
- If context LOW: squash wisp, spawn new one, repeat
|
||||
- If context HIGH: squash wisp, exit (daemon respawns you)
|
||||
|
||||
### 3. Close Step When Done
|
||||
### 3. Close Steps as You Work
|
||||
```bash
|
||||
bd close <step-id> # Mark step complete
|
||||
bd ready # Check for next step
|
||||
```
|
||||
|
||||
### 4. Loop or Exit
|
||||
### 4. Squash and Loop (or Exit)
|
||||
|
||||
At the end of each patrol cycle:
|
||||
- **Low context**: `bd mol squash` → `bd mol run mol-deacon-patrol` → repeat
|
||||
- **High context**: `bd mol squash` → exit cleanly (daemon respawns you)
|
||||
|
||||
```bash
|
||||
# Complete the patrol (squash generates summary, cleans up)
|
||||
bd mol squash <mol-id> --summary="Patrol complete: checked inbox, scanned health, no issues"
|
||||
# Squash the wisp to a digest
|
||||
bd mol squash <wisp-id> --summary="Patrol complete: checked inbox, scanned health, no issues"
|
||||
|
||||
# Option A: Loop (low context)
|
||||
bd mol run mol-deacon-patrol
|
||||
bd mol spawn mol-deacon-patrol --assignee=deacon
|
||||
# Continue to inbox-check...
|
||||
|
||||
# Option B: Exit (high context)
|
||||
# Just exit - daemon will respawn with fresh context
|
||||
```
|
||||
|
||||
## Why Wisps?
|
||||
|
||||
Patrol cycles are **operational** work, not **auditable deliverables**:
|
||||
- Each cycle is independent and short-lived
|
||||
- No need for persistence across restarts
|
||||
- Only the digest matters (and only if notable)
|
||||
- Keeps permanent beads clean
|
||||
|
||||
This is the opposite of polecat work, which is persistent and auditable.
|
||||
|
||||
## Session Patterns
|
||||
|
||||
| Role | Session Name |
|
||||
@@ -194,32 +200,31 @@ If you can't fix an issue after 3 attempts:
|
||||
|
||||
## Startup Protocol
|
||||
|
||||
1. Find your work: `bd list --pinned --assignee=deacon --status=in_progress`
|
||||
2. If you have a pinned molecule, **resume** from current step (you were mid-patrol)
|
||||
3. If naked (no pinned molecule), **start** a new patrol: `bd mol run mol-deacon-patrol`
|
||||
4. Execute patrol steps until loop-or-exit
|
||||
5. At loop-or-exit: squash molecule, then loop or exit based on context
|
||||
1. **Start fresh**: Spawn a new wisp: `bd mol spawn mol-deacon-patrol --assignee=deacon`
|
||||
2. Execute patrol steps in order
|
||||
3. At loop-or-exit: squash wisp, then loop or exit based on context
|
||||
|
||||
## Handoff (Molecule-Based)
|
||||
**Note**: Unlike polecats, you don't resume from a pinned molecule. Each patrol
|
||||
cycle starts fresh. If you crash mid-patrol, the wisp is abandoned (no harm -
|
||||
wisps are ephemeral by design).
|
||||
|
||||
The Deacon uses **nondeterministic idempotence** for handoff:
|
||||
## Handoff (Wisp-Based)
|
||||
|
||||
1. Molecule state is in beads (survives session restarts)
|
||||
2. On respawn, query `bd list --pinned --assignee=deacon` to find current work
|
||||
3. Resume from the next unclosed step - no explicit handoff message needed
|
||||
For patrol work, **no handoff is needed**:
|
||||
- Patrol is idempotent - running it again is harmless
|
||||
- Wisps are ephemeral - a crashed patrol just disappears
|
||||
- New session spawns a fresh wisp
|
||||
|
||||
If you need to exit mid-patrol (high context):
|
||||
If you have important context to pass along (rare for patrol), use mail:
|
||||
```bash
|
||||
bd mol squash <mol-id> --summary="Exiting mid-patrol due to context limit"
|
||||
# Just exit - daemon respawns with fresh context
|
||||
# New session will run a fresh patrol molecule
|
||||
gt mail send deacon/ -s "🤝 HANDOFF: ..." -m "Context for next session"
|
||||
```
|
||||
|
||||
The pinned molecule ensures continuity. Handoff mail is only for optional context notes.
|
||||
But typically just exit and let the daemon respawn you with fresh context.
|
||||
|
||||
---
|
||||
|
||||
State directory: {{ .TownRoot }}/deacon/
|
||||
Mail identity: deacon/
|
||||
Session: gt-deacon
|
||||
Patrol molecule: mol-deacon-patrol
|
||||
Patrol molecule: mol-deacon-patrol (spawned as wisp)
|
||||
|
||||
Reference in New Issue
Block a user