variantpool

import "github.com/jbcom/radioactive-ralph/internal/variantpool"

Package variantpool manages the subprocess lifecycle of spawned ralph variants (green, professor, fixit, etc.). Each spawned subprocess gets a lifeline pipe — when the supervising MCP server dies for any reason (clean exit, SIGKILL, OOM, crash), the child reads EOF on the pipe and self-terminates within a few seconds.

Per-platform belt-and-suspenders (Pdeathsig / kqueue NOTE_EXIT / Job Objects) is deferred to internal/proclife — the lifeline pipe is portable and sufficient for the portable/durable-foreground cases.

Index

type Options

Options configures a new Pool.

type Options struct {
    // Store is the plandag handle used for heartbeat + variant row
    // bookkeeping. Required.
    Store *plandag.Store

    // SessionID is the plandag sessions.id this pool owns. Every
    // spawned variant is registered under this session.
    SessionID string
}

type Pool

Pool owns every live subprocess the MCP server has spawned on behalf of this session. Callers get back a handle per spawn.

type Pool struct {
    // contains filtered or unexported fields
}

func New

func New(o Options) (*Pool, error)

New allocates a Pool. Callers must call Close() on shutdown so subprocess children receive SIGTERM and DB rows clean up.

func (*Pool) Close

func (p *Pool) Close(ctx context.Context) error

Close SIGTERMs every managed variant and waits up to graceShutdown for each to exit, SIGKILLing stragglers.

func (*Pool) Get

func (p *Pool) Get(id string) *Variant

Get returns the variant managed under id, or nil if the pool doesn’t know about it (e.g., it was killed or never spawned). Read-only — callers should treat the return value as a snapshot handle.

func (*Pool) Spawn

func (p *Pool) Spawn(ctx context.Context, o SpawnOpts) (*Variant, error)

Spawn launches a variant subprocess with a lifeline pipe attached on FD 3. Returns a Variant handle; the caller can Say/Kill/Status.

type SpawnOpts

SpawnOpts configures a Spawn call.

type SpawnOpts struct {
    // VariantName names the variant (green, professor, ...).
    VariantName string

    // ClaudeBin is the absolute path to the claude binary that the
    // variant will exec. The variantpool itself does NOT spawn
    // claude directly — it spawns a ralph _supervisor command that
    // owns the claude lifecycle. Empty defaults to looking up
    // "claude" on PATH inside the supervisor.
    ClaudeBin string

    // RalphBin is the absolute path to the radioactive_ralph binary.
    // Pool exec's it as "<ralph> _supervisor --variant <name>".
    RalphBin string

    // WorkingDir is the working directory for the subprocess.
    WorkingDir string

    // Env is merged with the parent environment. Pool adds
    // RALPH_LIFELINE_FD=3 automatically.
    Env []string

    // LogSink receives the subprocess stdout+stderr. Nil → os.Stderr.
    LogSink io.Writer
}

type Status

Status returns a snapshot of the subprocess state.

type Status struct {
    ID      string
    Name    string
    PID     int
    Running bool
}

type Variant

Variant is a managed subprocess handle.

type Variant struct {
    // ID is the plandag session_variants.id.
    ID  string

    // Name is the variant name (green, professor, ...).
    Name string
    // contains filtered or unexported fields
}

func (*Variant) Kill

func (v *Variant) Kill(ctx context.Context) error

Kill signals the subprocess to shut down. Closes the lifeline pipe (which alone causes a well-behaved child to exit), then SIGTERMs, then SIGKILLs after graceWait.

func (*Variant) ReadOne

func (v *Variant) ReadOne(buf []byte) (int, error)

ReadOne reads the next line of stdout. Callers typically loop calling this in a goroutine to drain output.

func (*Variant) Say

func (v *Variant) Say(msg string) error

Say writes a line to the subprocess’s stdin. The trailing \n is added if not present.

func (*Variant) Status

func (v *Variant) Status() Status

Status produces a lightweight read-only snapshot suitable for MCP tool responses.

Generated by gomarkdoc