spool

package
v0.8.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package spool persists capture payloads that could not be uploaded (network failure, offline) so they can be retried on the next SessionStart reconcile or `sync status`. Each entry is fully self-contained — it stores the RESOLVED target (workspace, directory, mode, payload, hash) so a later reconcile works even if the manifest changed (DX-7).

Files are 0600 in a 0700 per-user dir, never repo-relative or world-readable (SEC-7). Entries are capped, TTL-bounded, and capped on attempts; flushed entries are removed.

Index

Constants

View Source
const (
	// MaxEntries caps the spool so a persistent outage can't grow unbounded.
	MaxEntries = 200
	// TTL bounds how long an un-flushed entry survives.
	TTL = 14 * 24 * time.Hour
	// MaxAttempts bounds retries before an entry is dropped as poison.
	MaxAttempts = 8
)
View Source
const LedgerMax = 2000

LedgerMax bounds the captured-session ledger so it cannot grow without limit.

Variables

This section is empty.

Functions

func Add

func Add(e *Entry) (string, error)

Add writes (or overwrites) a spool entry for the payload. CreatedAt is set if unset. Returns the file path.

func Count

func Count() (int, error)

Count returns the number of live spooled entries.

func Dir

func Dir() (string, error)

Dir returns the 0700 spool directory under the CLI config dir.

Types

type Entry

type Entry struct {
	ClaudeSessionID string                 `json:"claudeSessionId"`
	WorkspaceID     string                 `json:"workspaceId"`
	DirectoryID     string                 `json:"directoryId,omitempty"`
	CaptureMode     capture.Mode           `json:"captureMode"`
	RepoURL         string                 `json:"repoUrl,omitempty"`
	ContentHash     string                 `json:"contentHash"`
	Payload         *capture.IngestRequest `json:"payload"`
	CreatedAt       time.Time              `json:"createdAt"`
	Attempts        int                    `json:"attempts"`

	// Reason records why the capture was spooled instead of uploaded (e.g.
	// "no capture credential stored", "upload failed: …", "manifest had no
	// workspace; used config default") so `sync status` / `sync doctor` can
	// explain a growing spool instead of failing silently.
	Reason string `json:"reason,omitempty"`
	// contains filtered or unexported fields
}

Entry is one spooled capture, self-contained for reconcile (DX-7).

func List

func List() ([]*Entry, error)

List returns all live spool entries, pruning expired or poison entries from disk as a side effect. Entries are returned oldest-first.

func (*Entry) MarkAttempt

func (e *Entry) MarkAttempt() error

MarkAttempt increments the attempt counter and rewrites the entry to disk so poison entries eventually age out via MaxAttempts.

func (*Entry) Remove

func (e *Entry) Remove() error

Remove deletes the entry's on-disk file (used after a successful flush).

type Ledger

type Ledger struct {
	Captured map[string]time.Time `json:"captured"`
	// contains filtered or unexported fields
}

Ledger records which claude_session_ids have already been captured, so the SessionStart reconcile (which is transcript-driven, HOOK-1) can skip sessions already uploaded and only flush genuinely-missing ones.

func LoadLedger

func LoadLedger() (*Ledger, error)

LoadLedger reads the ledger, returning an empty one if absent.

func (*Ledger) Has

func (l *Ledger) Has(sessionID string) bool

Has reports whether a session has already been captured.

func (*Ledger) Mark

func (l *Ledger) Mark(sessionID string)

Mark records a session as captured and trims the ledger to LedgerMax, dropping the oldest entries.

func (*Ledger) Save

func (l *Ledger) Save() error

Save atomically writes the ledger to disk.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL