Rename ephemeral -> wisp terminology throughout Gas Town

- .beads-ephemeral/ -> .beads-wisp/
- Rename doctor checks: EphemeralCheck -> WispCheck
- Update all docs to use 'transient' for polecats, 'wisp' for molecules
- Preserve 'ephemeral' only as descriptive adjective for wisps
- Steam engine metaphor: wisps are steam vapors that dissipate

Part of Christmas launch wisp terminology unification.
This commit is contained in:
Steve Yegge
2025-12-22 00:55:31 -08:00
parent ebbe886d81
commit 5d7291962a
21 changed files with 186 additions and 185 deletions

View File

@@ -5,7 +5,7 @@
## Your Role: CREW WORKER (joe in gastown) ## Your Role: CREW WORKER (joe in gastown)
You are a **crew worker** - the overseer's (human's) personal workspace within the You are a **crew worker** - the overseer's (human's) personal workspace within the
gastown rig. Unlike polecats which are witness-managed and ephemeral, you are: gastown rig. Unlike polecats which are witness-managed and transient, you are:
- **Persistent**: Your workspace is never auto-garbage-collected - **Persistent**: Your workspace is never auto-garbage-collected
- **User-managed**: The overseer controls your lifecycle, not the Witness - **User-managed**: The overseer controls your lifecycle, not the Witness

View File

@@ -357,8 +357,8 @@ Molecules follow a **states of matter** metaphor through their lifecycle:
┌───────────────┐ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Mol │ │ Wisp │ │ Mol │ │ Wisp │
│ (liquid) │ │ (gas) │ │ (liquid) │ │ (gas) │
│ durable │ │ ephemeral │ durable │ │ transient
│ main beads │ │ .beads-eph/ │ main beads │ │ .beads-wisp/
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │ │
bd mol squash bd mol squash bd mol squash bd mol squash
@@ -374,15 +374,15 @@ Molecules follow a **states of matter** metaphor through their lifecycle:
**Phase transitions:** **Phase transitions:**
- **Proto → Mol/Wisp** (`bd mol bond`): Instantiate a template into a running execution - **Proto → Mol/Wisp** (`bd mol bond`): Instantiate a template into a running execution
- **Mol → Digest** (`bd mol squash`): Compress completed work into permanent record - **Mol → Digest** (`bd mol squash`): Compress completed work into permanent record
- **Wisp → (evaporates)** (`bd mol squash` or `bd mol burn`): Ephemeral trace disappears - **Wisp → (evaporates)** (`bd mol squash` or `bd mol burn`): Transient trace disappears
### When to Use Mol vs Wisp ### When to Use Mol vs Wisp
The choice between **Mol** (durable) and **Wisp** (ephemeral) depends on the work's importance and audit requirements: The choice between **Mol** (durable) and **Wisp** (transient) depends on the work's importance and audit requirements:
| Aspect | Mol (Durable) | Wisp (Ephemeral) | | Aspect | Mol (Durable) | Wisp (Transient) |
|--------|---------------|------------------| |--------|---------------|------------------|
| **Storage** | Main `.beads/` database | `.beads-ephemeral/` directory | | **Storage** | Main `.beads/` database | `.beads-wisp/` directory |
| **Persistence** | Survives indefinitely | Evaporates on squash/burn | | **Persistence** | Survives indefinitely | Evaporates on squash/burn |
| **Git tracking** | Committed, synced | Never committed | | **Git tracking** | Committed, synced | Never committed |
| **Audit trail** | Full history preserved | Only digest (if squashed) | | **Audit trail** | Full history preserved | Only digest (if squashed) |
@@ -396,7 +396,7 @@ The choice between **Mol** (durable) and **Wisp** (ephemeral) depends on the wor
**Use Wisp for:** **Use Wisp for:**
- Orchestration tasks (witness patrols, health checks) - Orchestration tasks (witness patrols, health checks)
- Polecat work sessions (ephemeral by nature) - Polecat work sessions (transient by nature)
- Patrol loops (continuous monitoring) - Patrol loops (continuous monitoring)
- Routine operations (no audit value) - Routine operations (no audit value)
@@ -491,7 +491,7 @@ This is like a **distributed work queue** backed by beads:
### Wisp Molecules: Transient Execution Traces ### Wisp Molecules: Transient Execution Traces
**Wisps** are ephemeral execution traces - the "steam" in Gas Town's engine metaphor. When a molecule executes, it generates wisps: transient issues that capture the work being done. **Wisps** are transient execution traces - the "steam" in Gas Town's engine metaphor. Claude is fire; Claude Code is a Steam engine; Gas Town is a Steam Train, with Beads as the tracks. Wisps are steam vapors that dissipate after the work is done.
**Why wisps?** **Why wisps?**
- **Observability**: See what's happening during execution without cluttering the permanent ledger - **Observability**: See what's happening during execution without cluttering the permanent ledger
@@ -547,8 +547,8 @@ bd mol burn gt-abc123.exec-001 # Discard wisps without digest
**Wisp storage:** **Wisp storage:**
Wisps are stored in a per-rig ephemeral database: Wisps are stored in a per-rig wisp database:
- `<rig>/.beads-ephemeral/` - Separate from permanent beads, **gitignored** - `<rig>/.beads-wisp/` - Separate from permanent beads, **gitignored**
- Fast writes, no sync overhead - Fast writes, no sync overhead
- Auto-cleaned on squash/burn - Auto-cleaned on squash/burn
- Digests write to permanent beads - Digests write to permanent beads
@@ -1316,7 +1316,7 @@ Polecats are the workers that do actual implementation:
4. On completion, polecat generates summary and squashes wisps to digest 4. On completion, polecat generates summary and squashes wisps to digest
5. Request shutdown, get deleted 5. Request shutdown, get deleted
The polecat itself is ephemeral, and so is its execution trace (wisps). Only the digest survives. The polecat itself is transient, and so is its execution trace (wisps). Only the digest survives.
## Key Workflows ## Key Workflows
@@ -1694,7 +1694,7 @@ sequenceDiagram
After Witness kills session: After Witness kills session:
- Remove worktree: `git worktree remove polecats/<name>` - Remove worktree: `git worktree remove polecats/<name>`
- Delete branch: `git branch -d polecat/<name>` - Delete branch: `git branch -d polecat/<name>`
- Polecat ceases to exist (ephemeral) - Polecat ceases to exist (transient)
### 13. Resource-Constrained Worker Pool ### 13. Resource-Constrained Worker Pool
@@ -1807,7 +1807,7 @@ With daemon session cycling, the system can run autonomously for extended period
- **Workers cycle**: If individual tasks are very large - **Workers cycle**: If individual tasks are very large
- **Daemon persistence**: Survives all agent restarts - **Daemon persistence**: Survives all agent restarts
The daemon is the only truly persistent component. All agents are ephemeral sessions that hand off state via mail. The daemon is the only truly persistent component. All agents are transient sessions that hand off state via mail.
Work is a continuous stream - you can add new issues, spawn new workers, reprioritize the queue, all without "starting a new swarm" or managing batch boundaries. Work is a continuous stream - you can add new issues, spawn new workers, reprioritize the queue, all without "starting a new swarm" or managing batch boundaries.
@@ -1900,7 +1900,7 @@ gt handoff # Polecat requests shutdown (run when done)
gt session stop <p> # Kill polecat session (Witness uses this) gt session stop <p> # Kill polecat session (Witness uses this)
``` ```
**Note**: `gt wake` and `gt sleep` are deprecated - polecats are ephemeral, not pooled. **Note**: `gt wake` and `gt sleep` are deprecated - polecats are transient, not pooled.
### Landing & Merge Queue ### Landing & Merge Queue

View File

@@ -163,7 +163,7 @@ parallel: true
# Beads Cleanup # Beads Cleanup
Daily cleanup of ephemeral beads. Daily cleanup of wisp storage.
## Actions ## Actions

View File

@@ -92,7 +92,7 @@ bd mol bond <proto-id> [--wisp] [--assignee=<addr>]
- **Default (Mol)**: Creates a durable molecule tracked in the main `.beads/` database. - **Default (Mol)**: Creates a durable molecule tracked in the main `.beads/` database.
Steps become permanent issues that survive indefinitely. Steps become permanent issues that survive indefinitely.
- **With --wisp**: Creates an ephemeral molecule in `.beads-ephemeral/`. Steps are - **With --wisp**: Creates a wisp (transient molecule) in `.beads-wisp/`. Steps are
transient and will be cleaned up on squash or burn. transient and will be cleaned up on squash or burn.
**Examples:** **Examples:**
@@ -133,7 +133,7 @@ bd mol squash <mol-id> --summary='...'
- **For Mol (durable)**: Creates a digest issue in the permanent beads database. - **For Mol (durable)**: Creates a digest issue in the permanent beads database.
The digest contains the summary and links back to the original proto. The digest contains the summary and links back to the original proto.
- **For Wisp (ephemeral)**: Evaporates the wisp (deletes from `.beads-ephemeral/`) - **For Wisp (transient)**: Evaporates the wisp (deletes from `.beads-wisp/`)
and creates a digest in the permanent database. The execution trace is gone, and creates a digest in the permanent database. The execution trace is gone,
but the outcome is preserved. but the outcome is preserved.
@@ -181,7 +181,7 @@ bd mol burn <mol-id> [--reason='...']
- Discards all molecule state (steps, progress, artifacts) - Discards all molecule state (steps, progress, artifacts)
- No digest is created - the molecule leaves no permanent record - No digest is created - the molecule leaves no permanent record
- For Wisps: Simply deletes from `.beads-ephemeral/` - For Wisps: Simply deletes from `.beads-wisp/`
- For Mols: Marks as abandoned/closed without digest - For Mols: Marks as abandoned/closed without digest
**When to burn vs squash:** **When to burn vs squash:**

View File

@@ -159,7 +159,7 @@ gastown/
│ ├── spawn/ │ ├── spawn/
│ │ ├── new_polecat.md │ │ ├── new_polecat.md
│ │ ├── reuse_polecat.md │ │ ├── reuse_polecat.md
│ │ └── ephemeral_worker.md │ │ └── transient_worker.md
│ └── lifecycle/ │ └── lifecycle/
│ ├── handoff.md │ ├── handoff.md
│ ├── escalation.md │ ├── escalation.md
@@ -175,7 +175,7 @@ type PromptContext struct {
Role string Role string
RigName string RigName string
PolecatName string PolecatName string
Ephemeral bool Transient bool
IssueID string IssueID string
IssueTitle string IssueTitle string
// ... additional fields // ... additional fields
@@ -348,7 +348,7 @@ Crew workers are the overseer's personal workspaces - a new role that differs fr
## Your Role: CREW WORKER ({{ name }} in {{ rig }}) ## Your Role: CREW WORKER ({{ name }} in {{ rig }})
You are a **crew worker** - the overseer's (human's) personal workspace within the {{ rig }} rig. You are a **crew worker** - the overseer's (human's) personal workspace within the {{ rig }} rig.
Unlike polecats which are witness-managed and ephemeral, you are: Unlike polecats which are witness-managed and transient, you are:
- **Persistent**: Your workspace is never auto-garbage-collected - **Persistent**: Your workspace is never auto-garbage-collected
- **User-managed**: The overseer controls your lifecycle, not the Witness - **User-managed**: The overseer controls your lifecycle, not the Witness

View File

@@ -1,12 +1,12 @@
# Wisp Architecture: Ephemeral Molecule Storage # Wisp Architecture: Transient Molecule Storage
> Status: Design Spec v1 - December 2024 > Status: Design Spec v1 - December 2024
## Overview ## Overview
**Wisps** are ephemeral molecule execution traces - the "steam" in Gas Town's engine **Wisps** are transient molecule execution traces - the "steam" in Gas Town's engine
metaphor. This document specifies where wisps are stored, how they're managed, and metaphor. Claude is fire; Claude Code is a Steam engine; Gas Town is a Steam Train,
which roles use them. with Beads as the tracks. Wisps are steam vapors that dissipate after the work is done.
## Core Principle ## Core Principle
@@ -15,7 +15,7 @@ which roles use them.
| Artifact | Storage | Git Tracked | Purpose | | Artifact | Storage | Git Tracked | Purpose |
|----------|---------|-------------|---------| |----------|---------|-------------|---------|
| Issues | `.beads/issues.jsonl` | Yes | Permanent project history | | Issues | `.beads/issues.jsonl` | Yes | Permanent project history |
| Wisps | `.beads-ephemeral/issues.jsonl` | **No** | Transient execution traces | | Wisps | `.beads-wisp/issues.jsonl` | **No** | Transient execution traces |
| Digests | `.beads/issues.jsonl` | Yes | Compressed summaries of squashed wisps | | Digests | `.beads/issues.jsonl` | Yes | Compressed summaries of squashed wisps |
## Storage Architecture ## Storage Architecture
@@ -28,27 +28,27 @@ which roles use them.
│ ├── .beads/ # CANONICAL rig beads (versioned) │ ├── .beads/ # CANONICAL rig beads (versioned)
│ │ ├── issues.jsonl # Permanent issues + digests │ │ ├── issues.jsonl # Permanent issues + digests
│ │ ├── config.yaml │ │ ├── config.yaml
│ │ └── .gitignore # Excludes .beads-ephemeral │ │ └── .gitignore # Excludes .beads-wisp
│ │ │ │
│ └── .beads-ephemeral/ # GITIGNORED - local wisps │ └── .beads-wisp/ # GITIGNORED - local wisps
│ └── issues.jsonl # In-progress ephemeral molecules │ └── issues.jsonl # In-progress wisp molecules
├── refinery/rig/ # Refinery's clone ├── refinery/rig/ # Refinery's clone
│ ├── .beads/ # Inherits from mayor/rig │ ├── .beads/ # Inherits from mayor/rig
│ └── .beads-ephemeral/ # Refinery's local wisps │ └── .beads-wisp/ # Refinery's local wisps
├── witness/ # Witness (no clone needed) ├── witness/ # Witness (no clone needed)
│ └── .beads-ephemeral/ # Witness's local wisps │ └── .beads-wisp/ # Witness's local wisps
└── polecats/<name>/ # Polecat worktrees └── polecats/<name>/ # Polecat worktrees
├── .beads/ # Inherits from mayor/rig ├── .beads/ # Inherits from mayor/rig
└── .beads-ephemeral/ # Polecat's local wisps (if using wisps) └── .beads-wisp/ # Polecat's local wisps (if using wisps)
``` ```
### Key Points ### Key Points
1. **`.beads-ephemeral/` is gitignored** - Never synced, never versioned 1. **`.beads-wisp/` is gitignored** - Never synced, never versioned
2. **Each execution context has its own ephemeral store** - Process isolation 2. **Each execution context has its own wisp store** - Process isolation
3. **Digests go to canonical `.beads/`** - Permanent record after squash 3. **Digests go to canonical `.beads/`** - Permanent record after squash
4. **Wisps are deleted after squash/burn** - No accumulation 4. **Wisps are deleted after squash/burn** - No accumulation
@@ -56,24 +56,24 @@ which roles use them.
Add to `.beads/.gitignore`: Add to `.beads/.gitignore`:
``` ```
.beads-ephemeral/ .beads-wisp/
``` ```
Or add to rig-level `.gitignore`: Or add to rig-level `.gitignore`:
``` ```
**/.beads-ephemeral/ **/.beads-wisp/
``` ```
## Wisp Lifecycle ## Wisp Lifecycle
``` ```
bd mol bond <proto> --ephemeral bd mol bond <proto> --wisp
┌─────────────────────────┐ ┌─────────────────────────┐
│ .beads-ephemeral/ │ .beads-wisp/
│ └── issues.jsonl │ ← Wisp created here │ └── issues.jsonl │ ← Wisp created here
│ └── {id, ephemeral: true, ...} │ └── {id, wisp: true, ...}
└────────────┬────────────┘ └────────────┬────────────┘
┌────────┴────────┐ ┌────────┴────────┐
@@ -95,9 +95,9 @@ These roles have repetitive/cyclic work that would accumulate without wisps:
| Role | Molecule | Storage Location | Squash Frequency | | Role | Molecule | Storage Location | Squash Frequency |
|------|----------|------------------|------------------| |------|----------|------------------|------------------|
| **Deacon** | mol-deacon-patrol | mayor/rig/.beads-ephemeral/ | Per cycle | | **Deacon** | mol-deacon-patrol | mayor/rig/.beads-wisp/ | Per cycle |
| **Witness** | mol-witness-patrol | witness/.beads-ephemeral/ | Per cycle | | **Witness** | mol-witness-patrol | witness/.beads-wisp/ | Per cycle |
| **Refinery** | mol-refinery-cycle | refinery/rig/.beads-ephemeral/ | Per cycle | | **Refinery** | mol-refinery-cycle | refinery/rig/.beads-wisp/ | Per cycle |
### Roles That Use Regular Molecules ### Roles That Use Regular Molecules
@@ -126,8 +126,8 @@ Every role using wisps must implement this pattern:
```go ```go
func patrolCycle() { func patrolCycle() {
// 1. Bond ephemeral molecule // 1. Bond wisp molecule
mol := bdMolBond("mol-<role>-patrol", "--ephemeral") mol := bdMolBond("mol-<role>-patrol", "--wisp")
// 2. Execute cycle steps // 2. Execute cycle steps
for _, step := range mol.Steps { for _, step := range mol.Steps {
@@ -140,7 +140,7 @@ func patrolCycle() {
// 4. Squash - REQUIRED (this is the cleanup) // 4. Squash - REQUIRED (this is the cleanup)
bdMolSquash(mol.ID, "--summary", summary) bdMolSquash(mol.ID, "--summary", summary)
// Wisp deleted from .beads-ephemeral/ // Wisp deleted from .beads-wisp/
// Digest created in .beads/issues.jsonl // Digest created in .beads/issues.jsonl
// 5. Sleep until next cycle // 5. Sleep until next cycle
@@ -157,11 +157,11 @@ For this architecture to work, Beads needs:
### New Commands ### New Commands
```bash ```bash
# Bond with ephemeral flag # Bond with wisp flag (--ephemeral is an alias)
bd mol bond <proto> --ephemeral bd mol bond <proto> --wisp
# Creates in .beads-ephemeral/ instead of .beads/ # Creates in .beads-wisp/ instead of .beads/
# List ephemeral molecules # List wisps
bd wisp list bd wisp list
# Shows in-progress wisps # Shows in-progress wisps
@@ -172,28 +172,28 @@ bd wisp gc
### Storage Behavior ### Storage Behavior
| Command | With `--ephemeral` | Without | | Command | With `--wisp` | Without |
|---------|-------------------|---------| |---------|---------------|---------|
| `bd mol bond` | Creates in `.beads-ephemeral/` | Creates in `.beads/` | | `bd mol bond` | Creates in `.beads-wisp/` | Creates in `.beads/` |
| `bd mol step` | Updates in ephemeral | Updates in permanent | | `bd mol step` | Updates in wisp store | Updates in permanent |
| `bd mol squash` | Deletes from ephemeral, creates digest in permanent | Creates digest in permanent | | `bd mol squash` | Deletes from wisp, creates digest in permanent | Creates digest in permanent |
| `bd mol burn` | Deletes from ephemeral | Marks abandoned in permanent | | `bd mol burn` | Deletes from wisp | Marks abandoned in permanent |
### Config ### Config
```yaml ```yaml
# .beads/config.yaml # .beads/config.yaml
ephemeral: wisp:
enabled: true enabled: true
directory: ../.beads-ephemeral # Relative to .beads/ directory: ../.beads-wisp # Relative to .beads/
auto_gc: true # Clean orphans on bd init auto_gc: true # Clean orphans on bd init
``` ```
## Crash Recovery ## Crash Recovery
If a patrol crashes mid-cycle: If a patrol crashes mid-cycle:
1. **Wisp persists in `.beads-ephemeral/`** - Provides recovery breadcrumb 1. **Wisp persists in `.beads-wisp/`** - Provides recovery breadcrumb
2. **On restart, agent can:** 2. **On restart, agent can:**
- Resume from last step (if step tracking is granular) - Resume from last step (if step tracking is granular)
- Or burn and start fresh (simpler for patrol loops) - Or burn and start fresh (simpler for patrol loops)
@@ -232,9 +232,10 @@ bd list --type=digest --parent=gt-deacon-patrol
For existing Gas Town installations: For existing Gas Town installations:
1. **Add `.beads-ephemeral/` to gitignore** (immediate) 1. **Add `.beads-wisp/` to gitignore** (immediate)
2. **Update patrol runners to use `--ephemeral`** (as patched) 2. **Update patrol runners to use `--wisp`** (as patched)
3. **No migration of existing data** - Fresh start for ephemeral storage 3. **No migration of existing data** - Fresh start for wisp storage
4. **Optional**: Remove old `.beads-ephemeral/` directories
## Open Questions ## Open Questions
@@ -250,5 +251,5 @@ For existing Gas Town installations:
## Implementation Tracking ## Implementation Tracking
- **Beads**: bd-kwjh (Wisp storage: ephemeral molecule tracking) - **Beads**: bd-kwjh (Wisp storage: transient molecule tracking)
- **Gas Town**: gt-3x0z.9 (mol-deacon-patrol uses ephemeral) - **Gas Town**: gt-3x0z.9 (mol-deacon-patrol uses wisps)

View File

@@ -19,7 +19,7 @@ var crewCmd = &cobra.Command{
Short: "Manage crew workspaces (user-managed persistent workspaces)", Short: "Manage crew workspaces (user-managed persistent workspaces)",
Long: `Crew workers are user-managed persistent workspaces within a rig. Long: `Crew workers are user-managed persistent workspaces within a rig.
Unlike polecats which are witness-managed and ephemeral, crew workers are: Unlike polecats which are witness-managed and transient, crew workers are:
- Persistent: Not auto-garbage-collected - Persistent: Not auto-garbage-collected
- User-managed: Overseer controls lifecycle - User-managed: Overseer controls lifecycle
- Long-lived identities: recognizable names like dave, emma, fred - Long-lived identities: recognizable names like dave, emma, fred

View File

@@ -63,12 +63,12 @@ func runDoctor(cmd *cobra.Command, args []string) error {
d.Register(doctor.NewIdentityCollisionCheck()) d.Register(doctor.NewIdentityCollisionCheck())
d.Register(doctor.NewThemeCheck()) d.Register(doctor.NewThemeCheck())
// Ephemeral beads checks // Wisp storage checks
d.Register(doctor.NewEphemeralExistsCheck()) d.Register(doctor.NewWispExistsCheck())
d.Register(doctor.NewEphemeralGitCheck()) d.Register(doctor.NewWispGitCheck())
d.Register(doctor.NewEphemeralOrphansCheck()) d.Register(doctor.NewWispOrphansCheck())
d.Register(doctor.NewEphemeralSizeCheck()) d.Register(doctor.NewWispSizeCheck())
d.Register(doctor.NewEphemeralStaleCheck()) d.Register(doctor.NewWispStaleCheck())
// Run checks // Run checks
var report *doctor.Report var report *doctor.Report

View File

@@ -56,7 +56,7 @@ const HQGitignore = `# Gas Town HQ .gitignore
# Ignore: Git clones (polecats, mayor/refinery rigs), runtime state # Ignore: Git clones (polecats, mayor/refinery rigs), runtime state
# ============================================================================= # =============================================================================
# Runtime state files (ephemeral) # Runtime state files (transient)
# ============================================================================= # =============================================================================
**/state.json **/state.json
**/*.lock **/*.lock

View File

@@ -41,7 +41,7 @@ var polecatListCmd = &cobra.Command{
Short: "List polecats in a rig", Short: "List polecats in a rig",
Long: `List polecats in a rig or all rigs. Long: `List polecats in a rig or all rigs.
In the ephemeral model, polecats exist only while working. The list shows In the transient model, polecats exist only while working. The list shows
all currently active polecats with their states: all currently active polecats with their states:
- working: Actively working on an issue - working: Actively working on an issue
- done: Completed work, waiting for cleanup - done: Completed work, waiting for cleanup
@@ -91,7 +91,7 @@ var polecatWakeCmd = &cobra.Command{
Short: "(Deprecated) Resume a polecat to working state", Short: "(Deprecated) Resume a polecat to working state",
Long: `Resume a polecat to working state. Long: `Resume a polecat to working state.
DEPRECATED: In the ephemeral model, polecats are created fresh for each task DEPRECATED: In the transient model, polecats are created fresh for each task
via 'gt spawn'. This command is kept for backward compatibility. via 'gt spawn'. This command is kept for backward compatibility.
Transitions: done → working Transitions: done → working
@@ -107,7 +107,7 @@ var polecatSleepCmd = &cobra.Command{
Short: "(Deprecated) Mark polecat as done", Short: "(Deprecated) Mark polecat as done",
Long: `Mark polecat as done. Long: `Mark polecat as done.
DEPRECATED: In the ephemeral model, polecats use 'gt handoff' when complete, DEPRECATED: In the transient model, polecats use 'gt handoff' when complete,
which triggers automatic cleanup by the Witness. This command is kept for which triggers automatic cleanup by the Witness. This command is kept for
backward compatibility. backward compatibility.

View File

@@ -270,7 +270,7 @@ func outputWitnessContext(ctx RoleContext) {
fmt.Printf("You are the **Witness** for rig: %s\n\n", style.Bold.Render(ctx.Rig)) fmt.Printf("You are the **Witness** for rig: %s\n\n", style.Bold.Render(ctx.Rig))
fmt.Println("## Responsibilities") fmt.Println("## Responsibilities")
fmt.Println("- Monitor polecat health via heartbeat") fmt.Println("- Monitor polecat health via heartbeat")
fmt.Println("- Spawn ephemeral agents for stuck polecats") fmt.Println("- Spawn replacement agents for stuck polecats")
fmt.Println("- Report rig status to Mayor") fmt.Println("- Report rig status to Mayor")
fmt.Println() fmt.Println()
fmt.Println("## Key Commands") fmt.Println("## Key Commands")

View File

@@ -28,7 +28,7 @@ infrastructure agents are running:
• Mayor - Global work coordinator • Mayor - Global work coordinator
• Witnesses - Per-rig polecat managers • Witnesses - Per-rig polecat managers
Polecats are NOT started by this command - they are ephemeral workers Polecats are NOT started by this command - they are transient workers
spawned on demand by the Mayor or Witnesses. spawned on demand by the Mayor or Witnesses.
Running 'gt up' multiple times is safe - it only starts services that Running 'gt up' multiple times is safe - it only starts services that

View File

@@ -165,9 +165,9 @@ var deaconMOTDMessages = []string{
"Thanks for keeping the town running!", "Thanks for keeping the town running!",
"You are Gas Town's most critical role.", "You are Gas Town's most critical role.",
"You are the heart of Gas Town! Be watchful!", "You are the heart of Gas Town! Be watchful!",
"Tip: Polecats are ephemeral - spawn freely, kill liberally.", "Tip: Polecats are transient - spawn freely, kill liberally.",
"Tip: Witnesses monitor polecats; you monitor witnesses.", "Tip: Witnesses monitor polecats; you monitor witnesses.",
"Tip: Wisps are ephemeral molecules for patrol cycles.", "Tip: Wisps are transient molecules for patrol cycles.",
"The town sleeps soundly because you never do.", "The town sleeps soundly because you never do.",
"Tip: Mayor handles cross-rig coordination; you handle health.", "Tip: Mayor handles cross-rig coordination; you handle health.",
"Your vigilance keeps the agents honest.", "Your vigilance keeps the agents honest.",

View File

@@ -11,26 +11,26 @@ import (
"github.com/steveyegge/gastown/internal/config" "github.com/steveyegge/gastown/internal/config"
) )
// EphemeralExistsCheck verifies that .beads-ephemeral/ exists for each rig. // WispExistsCheck verifies that .beads-wisp/ exists for each rig.
type EphemeralExistsCheck struct { type WispExistsCheck struct {
FixableCheck FixableCheck
missingRigs []string // Cached for fix missingRigs []string // Cached for fix
} }
// NewEphemeralExistsCheck creates a new ephemeral exists check. // NewWispExistsCheck creates a new wisp exists check.
func NewEphemeralExistsCheck() *EphemeralExistsCheck { func NewWispExistsCheck() *WispExistsCheck {
return &EphemeralExistsCheck{ return &WispExistsCheck{
FixableCheck: FixableCheck{ FixableCheck: FixableCheck{
BaseCheck: BaseCheck{ BaseCheck: BaseCheck{
CheckName: "ephemeral-exists", CheckName: "wisp-exists",
CheckDescription: "Check if ephemeral beads directory exists for each rig", CheckDescription: "Check if wisp directory exists for each rig",
}, },
}, },
} }
} }
// Run checks if .beads-ephemeral/ exists for each rig. // Run checks if .beads-wisp/ exists for each rig.
func (c *EphemeralExistsCheck) Run(ctx *CheckContext) *CheckResult { func (c *WispExistsCheck) Run(ctx *CheckContext) *CheckResult {
c.missingRigs = nil // Reset cache c.missingRigs = nil // Reset cache
// Find all rigs // Find all rigs
@@ -55,8 +55,8 @@ func (c *EphemeralExistsCheck) Run(ctx *CheckContext) *CheckResult {
// Check each rig // Check each rig
var missing []string var missing []string
for _, rigName := range rigs { for _, rigName := range rigs {
ephemeralPath := filepath.Join(ctx.TownRoot, rigName, ".beads-ephemeral") wispPath := filepath.Join(ctx.TownRoot, rigName, ".beads-wisp")
if _, err := os.Stat(ephemeralPath); os.IsNotExist(err) { if _, err := os.Stat(wispPath); os.IsNotExist(err) {
missing = append(missing, rigName) missing = append(missing, rigName)
} }
} }
@@ -66,7 +66,7 @@ func (c *EphemeralExistsCheck) Run(ctx *CheckContext) *CheckResult {
return &CheckResult{ return &CheckResult{
Name: c.Name(), Name: c.Name(),
Status: StatusWarning, Status: StatusWarning,
Message: fmt.Sprintf("%d rig(s) missing ephemeral beads directory", len(missing)), Message: fmt.Sprintf("%d rig(s) missing wisp directory", len(missing)),
Details: missing, Details: missing,
FixHint: "Run 'gt doctor --fix' to create missing directories", FixHint: "Run 'gt doctor --fix' to create missing directories",
} }
@@ -75,23 +75,23 @@ func (c *EphemeralExistsCheck) Run(ctx *CheckContext) *CheckResult {
return &CheckResult{ return &CheckResult{
Name: c.Name(), Name: c.Name(),
Status: StatusOK, Status: StatusOK,
Message: fmt.Sprintf("All %d rig(s) have ephemeral beads directory", len(rigs)), Message: fmt.Sprintf("All %d rig(s) have wisp directory", len(rigs)),
} }
} }
// Fix creates missing .beads-ephemeral/ directories. // Fix creates missing .beads-wisp/ directories.
func (c *EphemeralExistsCheck) Fix(ctx *CheckContext) error { func (c *WispExistsCheck) Fix(ctx *CheckContext) error {
for _, rigName := range c.missingRigs { for _, rigName := range c.missingRigs {
ephemeralPath := filepath.Join(ctx.TownRoot, rigName, ".beads-ephemeral") wispPath := filepath.Join(ctx.TownRoot, rigName, ".beads-wisp")
if err := os.MkdirAll(ephemeralPath, 0755); err != nil { if err := os.MkdirAll(wispPath, 0755); err != nil {
return fmt.Errorf("creating %s: %w", ephemeralPath, err) return fmt.Errorf("creating %s: %w", wispPath, err)
} }
} }
return nil return nil
} }
// discoverRigs finds all registered rigs. // discoverRigs finds all registered rigs.
func (c *EphemeralExistsCheck) discoverRigs(townRoot string) ([]string, error) { func (c *WispExistsCheck) discoverRigs(townRoot string) ([]string, error) {
rigsPath := filepath.Join(townRoot, "mayor", "rigs.json") rigsPath := filepath.Join(townRoot, "mayor", "rigs.json")
data, err := os.ReadFile(rigsPath) data, err := os.ReadFile(rigsPath)
if err != nil { if err != nil {
@@ -113,26 +113,26 @@ func (c *EphemeralExistsCheck) discoverRigs(townRoot string) ([]string, error) {
return rigs, nil return rigs, nil
} }
// EphemeralGitCheck verifies that .beads-ephemeral/ is a valid git repo. // WispGitCheck verifies that .beads-wisp/ is a valid git repo.
type EphemeralGitCheck struct { type WispGitCheck struct {
FixableCheck FixableCheck
invalidRigs []string // Cached for fix invalidRigs []string // Cached for fix
} }
// NewEphemeralGitCheck creates a new ephemeral git check. // NewWispGitCheck creates a new wisp git check.
func NewEphemeralGitCheck() *EphemeralGitCheck { func NewWispGitCheck() *WispGitCheck {
return &EphemeralGitCheck{ return &WispGitCheck{
FixableCheck: FixableCheck{ FixableCheck: FixableCheck{
BaseCheck: BaseCheck{ BaseCheck: BaseCheck{
CheckName: "ephemeral-git", CheckName: "wisp-git",
CheckDescription: "Check if ephemeral beads directories are valid git repos", CheckDescription: "Check if wisp directories are valid git repos",
}, },
}, },
} }
} }
// Run checks if .beads-ephemeral/ directories are valid git repos. // Run checks if .beads-wisp/ directories are valid git repos.
func (c *EphemeralGitCheck) Run(ctx *CheckContext) *CheckResult { func (c *WispGitCheck) Run(ctx *CheckContext) *CheckResult {
c.invalidRigs = nil // Reset cache c.invalidRigs = nil // Reset cache
// Find all rigs // Find all rigs
@@ -154,18 +154,18 @@ func (c *EphemeralGitCheck) Run(ctx *CheckContext) *CheckResult {
} }
} }
// Check each rig that has an ephemeral dir // Check each rig that has a wisp dir
var invalid []string var invalid []string
var checked int var checked int
for _, rigName := range rigs { for _, rigName := range rigs {
ephemeralPath := filepath.Join(ctx.TownRoot, rigName, ".beads-ephemeral") wispPath := filepath.Join(ctx.TownRoot, rigName, ".beads-wisp")
if _, err := os.Stat(ephemeralPath); os.IsNotExist(err) { if _, err := os.Stat(wispPath); os.IsNotExist(err) {
continue // Skip if directory doesn't exist (handled by ephemeral-exists) continue // Skip if directory doesn't exist (handled by wisp-exists)
} }
checked++ checked++
// Check if it's a valid git repo // Check if it's a valid git repo
gitDir := filepath.Join(ephemeralPath, ".git") gitDir := filepath.Join(wispPath, ".git")
if _, err := os.Stat(gitDir); os.IsNotExist(err) { if _, err := os.Stat(gitDir); os.IsNotExist(err) {
invalid = append(invalid, rigName) invalid = append(invalid, rigName)
} }
@@ -175,7 +175,7 @@ func (c *EphemeralGitCheck) Run(ctx *CheckContext) *CheckResult {
return &CheckResult{ return &CheckResult{
Name: c.Name(), Name: c.Name(),
Status: StatusOK, Status: StatusOK,
Message: "No ephemeral beads directories to check", Message: "No wisp directories to check",
} }
} }
@@ -184,7 +184,7 @@ func (c *EphemeralGitCheck) Run(ctx *CheckContext) *CheckResult {
return &CheckResult{ return &CheckResult{
Name: c.Name(), Name: c.Name(),
Status: StatusWarning, Status: StatusWarning,
Message: fmt.Sprintf("%d ephemeral beads directory(ies) not initialized as git", len(invalid)), Message: fmt.Sprintf("%d wisp directory(ies) not initialized as git", len(invalid)),
Details: invalid, Details: invalid,
FixHint: "Run 'gt doctor --fix' to initialize git repos", FixHint: "Run 'gt doctor --fix' to initialize git repos",
} }
@@ -193,47 +193,47 @@ func (c *EphemeralGitCheck) Run(ctx *CheckContext) *CheckResult {
return &CheckResult{ return &CheckResult{
Name: c.Name(), Name: c.Name(),
Status: StatusOK, Status: StatusOK,
Message: fmt.Sprintf("All %d ephemeral beads directories are valid git repos", checked), Message: fmt.Sprintf("All %d wisp directories are valid git repos", checked),
} }
} }
// Fix initializes git repos in ephemeral directories. // Fix initializes git repos in wisp directories.
func (c *EphemeralGitCheck) Fix(ctx *CheckContext) error { func (c *WispGitCheck) Fix(ctx *CheckContext) error {
for _, rigName := range c.invalidRigs { for _, rigName := range c.invalidRigs {
ephemeralPath := filepath.Join(ctx.TownRoot, rigName, ".beads-ephemeral") wispPath := filepath.Join(ctx.TownRoot, rigName, ".beads-wisp")
cmd := exec.Command("git", "init") cmd := exec.Command("git", "init")
cmd.Dir = ephemeralPath cmd.Dir = wispPath
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
return fmt.Errorf("initializing git in %s: %w", ephemeralPath, err) return fmt.Errorf("initializing git in %s: %w", wispPath, err)
} }
// Create config.yaml for ephemeral beads // Create config.yaml for wisp storage
configPath := filepath.Join(ephemeralPath, "config.yaml") configPath := filepath.Join(wispPath, "config.yaml")
configContent := "ephemeral: true\n# No sync-branch - ephemeral is local only\n" configContent := "wisp: true\n# No sync-branch - wisps are local only\n"
if err := os.WriteFile(configPath, []byte(configContent), 0644); err != nil { if err := os.WriteFile(configPath, []byte(configContent), 0644); err != nil {
return fmt.Errorf("creating config.yaml in %s: %w", ephemeralPath, err) return fmt.Errorf("creating config.yaml in %s: %w", wispPath, err)
} }
} }
return nil return nil
} }
// EphemeralOrphansCheck detects molecules started but never squashed (>24h old). // WispOrphansCheck detects molecules started but never squashed (>24h old).
type EphemeralOrphansCheck struct { type WispOrphansCheck struct {
BaseCheck BaseCheck
} }
// NewEphemeralOrphansCheck creates a new ephemeral orphans check. // NewWispOrphansCheck creates a new wisp orphans check.
func NewEphemeralOrphansCheck() *EphemeralOrphansCheck { func NewWispOrphansCheck() *WispOrphansCheck {
return &EphemeralOrphansCheck{ return &WispOrphansCheck{
BaseCheck: BaseCheck{ BaseCheck: BaseCheck{
CheckName: "ephemeral-orphans", CheckName: "wisp-orphans",
CheckDescription: "Check for orphaned molecules (>24h old, never squashed)", CheckDescription: "Check for orphaned wisps (>24h old, never squashed)",
}, },
} }
} }
// Run checks for orphaned molecules. // Run checks for orphaned wisps.
func (c *EphemeralOrphansCheck) Run(ctx *CheckContext) *CheckResult { func (c *WispOrphansCheck) Run(ctx *CheckContext) *CheckResult {
rigs, err := discoverRigs(ctx.TownRoot) rigs, err := discoverRigs(ctx.TownRoot)
if err != nil { if err != nil {
return &CheckResult{ return &CheckResult{
@@ -256,13 +256,13 @@ func (c *EphemeralOrphansCheck) Run(ctx *CheckContext) *CheckResult {
cutoff := time.Now().Add(-24 * time.Hour) cutoff := time.Now().Add(-24 * time.Hour)
for _, rigName := range rigs { for _, rigName := range rigs {
ephemeralPath := filepath.Join(ctx.TownRoot, rigName, ".beads-ephemeral") wispPath := filepath.Join(ctx.TownRoot, rigName, ".beads-wisp")
if _, err := os.Stat(ephemeralPath); os.IsNotExist(err) { if _, err := os.Stat(wispPath); os.IsNotExist(err) {
continue continue
} }
// Look for molecule directories or issue files older than 24h // Look for molecule directories or issue files older than 24h
issuesPath := filepath.Join(ephemeralPath, "issues.jsonl") issuesPath := filepath.Join(wispPath, "issues.jsonl")
info, err := os.Stat(issuesPath) info, err := os.Stat(issuesPath)
if err != nil { if err != nil {
continue // No issues file continue // No issues file
@@ -279,7 +279,7 @@ func (c *EphemeralOrphansCheck) Run(ctx *CheckContext) *CheckResult {
return &CheckResult{ return &CheckResult{
Name: c.Name(), Name: c.Name(),
Status: StatusWarning, Status: StatusWarning,
Message: fmt.Sprintf("%d rig(s) have stale ephemeral data (>24h old)", len(orphans)), Message: fmt.Sprintf("%d rig(s) have stale wisp data (>24h old)", len(orphans)),
Details: orphans, Details: orphans,
FixHint: "Manual review required - these may contain unsquashed work", FixHint: "Manual review required - these may contain unsquashed work",
} }
@@ -288,27 +288,27 @@ func (c *EphemeralOrphansCheck) Run(ctx *CheckContext) *CheckResult {
return &CheckResult{ return &CheckResult{
Name: c.Name(), Name: c.Name(),
Status: StatusOK, Status: StatusOK,
Message: "No orphaned molecules found", Message: "No orphaned wisps found",
} }
} }
// EphemeralSizeCheck warns if ephemeral repo is too large (>100MB). // WispSizeCheck warns if wisp repo is too large (>100MB).
type EphemeralSizeCheck struct { type WispSizeCheck struct {
BaseCheck BaseCheck
} }
// NewEphemeralSizeCheck creates a new ephemeral size check. // NewWispSizeCheck creates a new wisp size check.
func NewEphemeralSizeCheck() *EphemeralSizeCheck { func NewWispSizeCheck() *WispSizeCheck {
return &EphemeralSizeCheck{ return &WispSizeCheck{
BaseCheck: BaseCheck{ BaseCheck: BaseCheck{
CheckName: "ephemeral-size", CheckName: "wisp-size",
CheckDescription: "Check if ephemeral beads directories are too large (>100MB)", CheckDescription: "Check if wisp directories are too large (>100MB)",
}, },
} }
} }
// Run checks the size of ephemeral beads directories. // Run checks the size of wisp directories.
func (c *EphemeralSizeCheck) Run(ctx *CheckContext) *CheckResult { func (c *WispSizeCheck) Run(ctx *CheckContext) *CheckResult {
rigs, err := discoverRigs(ctx.TownRoot) rigs, err := discoverRigs(ctx.TownRoot)
if err != nil { if err != nil {
return &CheckResult{ return &CheckResult{
@@ -331,12 +331,12 @@ func (c *EphemeralSizeCheck) Run(ctx *CheckContext) *CheckResult {
var oversized []string var oversized []string
for _, rigName := range rigs { for _, rigName := range rigs {
ephemeralPath := filepath.Join(ctx.TownRoot, rigName, ".beads-ephemeral") wispPath := filepath.Join(ctx.TownRoot, rigName, ".beads-wisp")
if _, err := os.Stat(ephemeralPath); os.IsNotExist(err) { if _, err := os.Stat(wispPath); os.IsNotExist(err) {
continue continue
} }
size, err := dirSize(ephemeralPath) size, err := dirSize(wispPath)
if err != nil { if err != nil {
continue continue
} }
@@ -351,7 +351,7 @@ func (c *EphemeralSizeCheck) Run(ctx *CheckContext) *CheckResult {
return &CheckResult{ return &CheckResult{
Name: c.Name(), Name: c.Name(),
Status: StatusWarning, Status: StatusWarning,
Message: fmt.Sprintf("%d rig(s) have oversized ephemeral directories", len(oversized)), Message: fmt.Sprintf("%d rig(s) have oversized wisp directories", len(oversized)),
Details: oversized, Details: oversized,
FixHint: "Consider cleaning up old completed molecules", FixHint: "Consider cleaning up old completed molecules",
} }
@@ -360,27 +360,27 @@ func (c *EphemeralSizeCheck) Run(ctx *CheckContext) *CheckResult {
return &CheckResult{ return &CheckResult{
Name: c.Name(), Name: c.Name(),
Status: StatusOK, Status: StatusOK,
Message: "All ephemeral directories within size limits", Message: "All wisp directories within size limits",
} }
} }
// EphemeralStaleCheck detects molecules with no activity in the last hour. // WispStaleCheck detects molecules with no activity in the last hour.
type EphemeralStaleCheck struct { type WispStaleCheck struct {
BaseCheck BaseCheck
} }
// NewEphemeralStaleCheck creates a new ephemeral stale check. // NewWispStaleCheck creates a new wisp stale check.
func NewEphemeralStaleCheck() *EphemeralStaleCheck { func NewWispStaleCheck() *WispStaleCheck {
return &EphemeralStaleCheck{ return &WispStaleCheck{
BaseCheck: BaseCheck{ BaseCheck: BaseCheck{
CheckName: "ephemeral-stale", CheckName: "wisp-stale",
CheckDescription: "Check for stale molecules (no activity in last hour)", CheckDescription: "Check for stale wisps (no activity in last hour)",
}, },
} }
} }
// Run checks for stale molecules. // Run checks for stale wisps.
func (c *EphemeralStaleCheck) Run(ctx *CheckContext) *CheckResult { func (c *WispStaleCheck) Run(ctx *CheckContext) *CheckResult {
rigs, err := discoverRigs(ctx.TownRoot) rigs, err := discoverRigs(ctx.TownRoot)
if err != nil { if err != nil {
return &CheckResult{ return &CheckResult{
@@ -403,15 +403,15 @@ func (c *EphemeralStaleCheck) Run(ctx *CheckContext) *CheckResult {
cutoff := time.Now().Add(-1 * time.Hour) cutoff := time.Now().Add(-1 * time.Hour)
for _, rigName := range rigs { for _, rigName := range rigs {
ephemeralPath := filepath.Join(ctx.TownRoot, rigName, ".beads-ephemeral") wispPath := filepath.Join(ctx.TownRoot, rigName, ".beads-wisp")
if _, err := os.Stat(ephemeralPath); os.IsNotExist(err) { if _, err := os.Stat(wispPath); os.IsNotExist(err) {
continue continue
} }
// Check for any recent activity in the ephemeral directory // Check for any recent activity in the wisp directory
// We look at the most recent modification time of any file // We look at the most recent modification time of any file
var mostRecent time.Time var mostRecent time.Time
_ = filepath.Walk(ephemeralPath, func(path string, info os.FileInfo, err error) error { _ = filepath.Walk(wispPath, func(path string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return nil return nil
} }
@@ -432,7 +432,7 @@ func (c *EphemeralStaleCheck) Run(ctx *CheckContext) *CheckResult {
return &CheckResult{ return &CheckResult{
Name: c.Name(), Name: c.Name(),
Status: StatusWarning, Status: StatusWarning,
Message: fmt.Sprintf("%d rig(s) have stale ephemeral activity", len(stale)), Message: fmt.Sprintf("%d rig(s) have stale wisp activity", len(stale)),
Details: stale, Details: stale,
FixHint: "Check if polecats are stuck or crashed", FixHint: "Check if polecats are stuck or crashed",
} }
@@ -441,7 +441,7 @@ func (c *EphemeralStaleCheck) Run(ctx *CheckContext) *CheckResult {
return &CheckResult{ return &CheckResult{
Name: c.Name(), Name: c.Name(),
Status: StatusOK, Status: StatusOK,
Message: "No stale ephemeral activity detected", Message: "No stale wisp activity detected",
} }
} }

View File

@@ -452,7 +452,7 @@ func (m *Manager) AssignIssue(name, issue string) error {
} }
// ClearIssue removes the issue assignment from a polecat. // ClearIssue removes the issue assignment from a polecat.
// In the ephemeral model, this transitions to Done state for cleanup. // In the transient model, this transitions to Done state for cleanup.
// This clears the assignee from the currently assigned issue in beads. // This clears the assignee from the currently assigned issue in beads.
// If beads is not available, this is a no-op. // If beads is not available, this is a no-op.
func (m *Manager) ClearIssue(name string) error { func (m *Manager) ClearIssue(name string) error {
@@ -485,7 +485,7 @@ func (m *Manager) ClearIssue(name string) error {
} }
// Wake transitions a polecat from idle to active. // Wake transitions a polecat from idle to active.
// Deprecated: In the ephemeral model, polecats start in working state. // Deprecated: In the transient model, polecats start in working state.
// This method is kept for backward compatibility with existing polecats. // This method is kept for backward compatibility with existing polecats.
func (m *Manager) Wake(name string) error { func (m *Manager) Wake(name string) error {
polecat, err := m.Get(name) polecat, err := m.Get(name)
@@ -502,7 +502,7 @@ func (m *Manager) Wake(name string) error {
} }
// Sleep transitions a polecat from active to idle. // Sleep transitions a polecat from active to idle.
// Deprecated: In the ephemeral model, polecats are deleted when done. // Deprecated: In the transient model, polecats are deleted when done.
// This method is kept for backward compatibility. // This method is kept for backward compatibility.
func (m *Manager) Sleep(name string) error { func (m *Manager) Sleep(name string) error {
polecat, err := m.Get(name) polecat, err := m.Get(name)

View File

@@ -4,12 +4,12 @@ package polecat
import "time" import "time"
// State represents the current state of a polecat. // State represents the current state of a polecat.
// In the ephemeral model, polecats exist only while working. // In the transient model, polecats exist only while working.
type State string type State string
const ( const (
// StateWorking means the polecat is actively working on an issue. // StateWorking means the polecat is actively working on an issue.
// This is the initial and primary state for ephemeral polecats. // This is the initial and primary state for transient polecats.
StateWorking State = "working" StateWorking State = "working"
// StateDone means the polecat has completed its assigned work // StateDone means the polecat has completed its assigned work
@@ -31,7 +31,7 @@ func (s State) IsWorking() bool {
} }
// IsActive returns true if the polecat session is actively working. // IsActive returns true if the polecat session is actively working.
// For ephemeral polecats, this is true for working state and // For transient polecats, this is true for working state and
// legacy idle/active states (treated as working). // legacy idle/active states (treated as working).
func (s State) IsActive() bool { func (s State) IsActive() bool {
return s == StateWorking || s == StateIdle || s == StateActive return s == StateWorking || s == StateIdle || s == StateActive

View File

@@ -5,7 +5,7 @@
## Your Role: CREW WORKER ({{ .Polecat }} in {{ .RigName }}) ## Your Role: CREW WORKER ({{ .Polecat }} in {{ .RigName }})
You are a **crew worker** - the overseer's (human's) personal workspace within the You are a **crew worker** - the overseer's (human's) personal workspace within the
{{ .RigName }} rig. Unlike polecats which are witness-managed and ephemeral, you are: {{ .RigName }} rig. Unlike polecats which are witness-managed and transient, you are:
- **Persistent**: Your workspace is never auto-garbage-collected - **Persistent**: Your workspace is never auto-garbage-collected
- **User-managed**: The overseer controls your lifecycle, not the Witness - **User-managed**: The overseer controls your lifecycle, not the Witness
@@ -26,7 +26,7 @@ Town ({{ .TownRoot }})
│ ├── .beads/ ← Issue tracking (you have write access) │ ├── .beads/ ← Issue tracking (you have write access)
│ ├── crew/ │ ├── crew/
│ │ └── {{ .Polecat }}/ ← You are here (your git clone) │ │ └── {{ .Polecat }}/ ← You are here (your git clone)
│ ├── polecats/ ← Ephemeral workers (not you) │ ├── polecats/ ← Transient workers (not you)
│ ├── refinery/ ← Merge queue processor │ ├── refinery/ ← Merge queue processor
│ └── witness/ ← Polecat lifecycle (doesn't monitor you) │ └── witness/ ← Polecat lifecycle (doesn't monitor you)
``` ```
@@ -141,7 +141,7 @@ Before ending your session:
## Tips ## Tips
- **You own your workspace**: Unlike polecats, you're not ephemeral. Keep it organized. - **You own your workspace**: Unlike polecats, you're not transient. Keep it organized.
- **Handoff liberally**: When in doubt, write a handoff mail. Context is precious. - **Handoff liberally**: When in doubt, write a handoff mail. Context is precious.
- **Stay in sync**: Pull from upstream regularly to avoid merge conflicts. - **Stay in sync**: Pull from upstream regularly to avoid merge conflicts.
- **Ask for help**: No Witness means no automatic escalation. Reach out proactively. - **Ask for help**: No Witness means no automatic escalation. Reach out proactively.

View File

@@ -553,7 +553,7 @@ func extractPolecatName(body string) string {
return "" return ""
} }
// cleanupPolecat performs the full cleanup sequence for an ephemeral polecat. // cleanupPolecat performs the full cleanup sequence for a transient polecat.
// 1. Check for uncommitted work (stubbornly refuses to lose work) // 1. Check for uncommitted work (stubbornly refuses to lose work)
// 2. Kill session // 2. Kill session
// 3. Remove worktree // 3. Remove worktree

View File

@@ -4,7 +4,7 @@
## Your Role: CREW WORKER ({{ name }} in {{ rig }}) ## Your Role: CREW WORKER ({{ name }} in {{ rig }})
You are a **crew worker** - the overseer's (human's) personal workspace within the {{ rig }} rig. Unlike polecats which are witness-managed and ephemeral, you are: You are a **crew worker** - the overseer's (human's) personal workspace within the {{ rig }} rig. Unlike polecats which are witness-managed and transient, you are:
- **Persistent**: Your workspace is never auto-garbage-collected - **Persistent**: Your workspace is never auto-garbage-collected
- **User-managed**: The overseer controls your lifecycle, not the Witness - **User-managed**: The overseer controls your lifecycle, not the Witness
@@ -157,7 +157,7 @@ Before ending your session:
## Tips ## Tips
- **You own your workspace**: Unlike polecats, you're not ephemeral. Keep it organized. - **You own your workspace**: Unlike polecats, you're not transient. Keep it organized.
- **Handoff liberally**: When in doubt, write a handoff mail. Context is precious. - **Handoff liberally**: When in doubt, write a handoff mail. Context is precious.
- **Stay in sync**: Pull from upstream regularly to avoid merge conflicts. - **Stay in sync**: Pull from upstream regularly to avoid merge conflicts.
- **Ask for help**: No Witness means no automatic escalation. Reach out proactively. - **Ask for help**: No Witness means no automatic escalation. Reach out proactively.

View File

@@ -4,10 +4,10 @@
## Your Role: POLECAT ({{ name }} in {{ rig }}) ## Your Role: POLECAT ({{ name }} in {{ rig }})
You are a **polecat** - an ephemeral worker agent in the Gas Town swarm. You are: You are a **polecat** - a transient worker agent in the Gas Town swarm. You are:
- **Task-focused**: You work on one assigned issue at a time - **Task-focused**: You work on one assigned issue at a time
- **Ephemeral**: When your work is done, you may be decommissioned - **Transient**: When your work is done, you may be decommissioned
- **Witness-managed**: The Witness monitors your progress and can nudge or reassign you - **Witness-managed**: The Witness monitors your progress and can nudge or reassign you
- **Part of a swarm**: Other polecats may be working on related issues in parallel - **Part of a swarm**: Other polecats may be working on related issues in parallel

View File

@@ -260,8 +260,8 @@ Before killing ANY polecat session, verify:
**If all checks pass:** **If all checks pass:**
1. Kill session: `tmux kill-session -t gt-{{ rig }}-<name>` 1. Kill session: `tmux kill-session -t gt-{{ rig }}-<name>`
2. Remove worktree: `git worktree remove polecats/<name>` (if ephemeral) 2. Remove worktree: `git worktree remove polecats/<name>` (if transient)
3. Delete branch: `git branch -d polecat/<name>` (if ephemeral) 3. Delete branch: `git branch -d polecat/<name>` (if transient)
--- ---
@@ -303,7 +303,7 @@ tmux capture-pane -t gt-{{ rig }}-<name> -p | tail -40
# Session control # Session control
tmux kill-session -t gt-{{ rig }}-<name> tmux kill-session -t gt-{{ rig }}-<name>
# Worktree cleanup (for ephemeral polecats) # Worktree cleanup (for transient polecats)
git worktree remove polecats/<name> git worktree remove polecats/<name>
git branch -d polecat/<name> git branch -d polecat/<name>