# What Is a Wisp in Beads? Understanding Ephemeral Workflows

> Discover what a Wisp is in Beads. Learn about ephemeral, non-persisted workflows managed locally with automatic cleanup via TTL policies. Explore this key Beads concept now.

- Repository: [Gas Town Hall/beads](https://github.com/gastownhall/beads)
- Tags: deep-dive
- Published: 2026-04-27

---

**A Wisp is Beads’ concept for an ephemeral, non-persisted workflow represented as an issue with `Ephemeral=true`, stored in a local-only `.beads-wisp/` directory that is git-ignored and automatically cleaned up via TTL-based policies.**

In the Beads issue tracking system, not all work is meant to persist forever. For temporary experiments, CI/CD coordination, and agent-to-agent communication, the repository implements **Wisps**—a special class of ephemeral issues that exist only in your local environment. Understanding this concept is essential for developers building automated workflows or managing short-lived operational tasks.

## What Defines a Wisp?

A Wisp is fundamentally a regular Beads issue with one critical distinction: the **`Ephemeral=true`** flag. This single attribute routes all queries to a separate **wisps table** (stored under `.beads-wisp/`) and ensures the data never syncs to remote repositories.

The storage location is explicitly excluded from version control. Because the directory is git-ignored, Wisps remain strictly local to the machine where they are created. This makes them ideal for throw-away work such as local debugging sessions, temporary coordination between automation agents, or short-lived CI/CD steps that do not require historical persistence.

## Wisp Classification and TTL Policies

Every Wisp carries a **`WispType`** that determines its Time-To-Live (TTL) compaction policy. These definitions live in [`internal/types/types.go`](https://github.com/gastownhall/beads/blob/main/internal/types/types.go) (lines 68‑85), categorizing Wisps by their forensic value and churn rate.

### High-Churn Types (6 Hour TTL)

Types in this category generate frequent updates with low long-term value.

- **`heartbeat`** – Regular health checks from agents
- **`ping`** – Connectivity verification signals

### Operational State Types (24 Hour TTL)

These capture medium-term operational data useful for daily monitoring.

- **`patrol`** – Automated scanning results
- **`gc_report`** – Garbage collection summaries

### Significant Event Types (7 Day TTL)

High-value events requiring longer retention for debugging and auditing.

- **`recovery`** – System recovery events
- **`error`** – Critical error reports
- **`escalation`** – Alert escalation chains

## Storage Layer Implementation

The core implementation treats Wisps as issues filtered by the ephemeral flag, ensuring seamless integration with existing search machinery while maintaining strict isolation.

### Core Storage Functions

In [`internal/storage/dolt/wisps.go`](https://github.com/gastownhall/beads/blob/main/internal/storage/dolt/wisps.go) (lines 63‑68), the storage layer exposes dedicated methods that wrap the generic issue search with wisp-specific routing.

```go
func (s *DoltStore) ListWisps(ctx context.Context, filter types.WispFilter) ([]*types.Issue, error) {
    issueFilter := issueops.WispFilterToIssueFilter(filter)
    return s.searchWisps(ctx, "", issueFilter)
}

```

### Filter Conversion and Routing

The guarantee that all Wisp queries hit the correct table lives in [`internal/storage/issueops/wisp_filter_convert.go`](https://github.com/gastownhall/beads/blob/main/internal/storage/issueops/wisp_filter_convert.go) (lines 5‑25). This helper force-injects `Ephemeral=true` into every filter, ensuring that even if a caller forgets to set the flag, the query is automatically routed to the wisps table rather than the permanent issues table.

```go
// The returned filter always has Ephemeral=true so queries are routed to the wisps table.

```

Additional instrumentation appears in [`internal/telemetry/storage.go`](https://github.com/gastownhall/beads/blob/main/internal/telemetry/storage.go) (lines 441‑447), which adds tracing around `ListWisps` calls for observability.

## Working with Wisps

Wisps support both CLI interaction for manual debugging and programmatic access for automation.

### CLI Commands

Create a new Wisp from a formula file for ad-hoc tasks.

```bash
bd wisp create quick-check --var target=auth-module

```

List active (non-closed) Wisps in human-readable or JSON format.

```bash
bd wisp list
bd wisp list --json

```

Delete a specific Wisp manually when cleanup is required immediately.

```bash
bd wisp delete bd-wisp-abc123

```

### Programmatic Access

Use the Go API to query Wisps with type and status filters. The following example demonstrates retrieving active task-type Wisps using the storage interface.

```go
package main

import (
    "context"
    "log"

    "github.com/gastownhall/beads/internal/types"
    "github.com/gastownhall/beads/internal/storage"
)

func main() {
    ctx := context.Background()
    var store storage.Storage // Assume initialized DoltStore

    // Retrieve all active wisps (default filter excludes closed)
    wisps, err := store.ListWisps(ctx, types.WispFilter{})
    if err != nil {
        log.Fatalf("ListWisps failed: %v", err)
    }

    for _, w := range wisps {
        log.Printf("Wisp %s – %s (type=%s)", w.ID, w.Title, w.WispType)
    }
}

```

Filter by specific criteria using the `WispFilter` struct fields.

```go
filter := types.WispFilter{
    Type:          ptrIssueType(types.TypeTask),
    Status:        ptrStatus(types.StatusInProgress),
    IncludeClosed: false,
}
wisps, _ := store.ListWisps(ctx, filter)

```

## Summary

- A **Wisp** is an ephemeral issue marked with `Ephemeral=true`, stored locally in `.beads-wisp/` and never synced to remotes.
- **TTL policies** vary by `WispType`, ranging from 6 hours for heartbeats to 7 days for escalations.
- **Storage isolation** is enforced by [`internal/storage/issueops/wisp_filter_convert.go`](https://github.com/gastownhall/beads/blob/main/internal/storage/issueops/wisp_filter_convert.go), which guarantees all queries route to the wisps table.
- **APIs** include `ListWisps`, `GetWisp`, and `DeleteWisp` in [`internal/storage/dolt/wisps.go`](https://github.com/gastownhall/beads/blob/main/internal/storage/dolt/wisps.go), available via CLI and Go SDK.

## Frequently Asked Questions

### How is a Wisp different from a regular Beads issue?

A Wisp is technically a regular issue with the `Ephemeral=true` flag set, but it is stored in a separate table (`.beads-wisp/`) that is git-ignored. Unlike standard issues, Wisps have automatic TTL-based expiration and are hidden by default when querying closed items unless the `--all` flag is used.

### Can Wisps be synced to a remote repository?

No. By design, the `.beads-wisp/` directory is git-ignored, meaning Wisps remain strictly local to the machine where they are created. This ensures temporary workflows and agent coordination do not pollute the shared repository history or trigger unnecessary CI runs.

### How do I clean up old Wisps manually?

Use the CLI command `bd wisp delete <wisp-id>` for immediate removal of specific Wisps. While the system automatically compacts expired Wisps based on their `WispType` TTL, manual deletion is useful when you need to free space or clear sensitive temporary data before the TTL expires.

### What happens when a Wisp reaches its TTL?

Once a Wisp exceeds its Time-To-Live based on its `WispType` classification (6 hours, 24 hours, or 7 days), the system automatically expires and cleans it up during compaction cycles. Closed Wisps are also hidden from default list views, though they persist in the local database until the TTL-driven garbage collection removes them.