dispatch

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: May 28, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package dispatch hosts the reusable dispatch subsystem: fire `claude` against a real open GitHub issue, in the matching local checkout,

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// Runner executes subprocesses (gh, git, open). Required.
	Runner *shell.Runner

	// Wrap applies the consumer's verb pipeline (argv validation, audit,
	// commit-scope resolution) to a verb.Spec the package builds. coily
	Wrap func(verb.Spec) cli.ActionFunc

	// AllowedOwner is the GitHub org dispatch will accept issue refs from.
	// This is the security claim, not a knob: dispatch refuses anything
	AllowedOwner string

	// ForgejoBaseURL enables Forgejo issue refs when set (scheme://host).
	// Requires FetchForgejoIssue.
	ForgejoBaseURL string

	// FetchForgejoIssue resolves a Forgejo issue. 404-shaped errors
	// (message contains "404") fall back to GitHub for shortform refs.
	FetchForgejoIssue func(ctx context.Context, owner, repo string, number int) (*Issue, error)

	// RepoPath resolves a repo name to its expected local checkout. The
	// consumer owns the workspace layout. Required.
	RepoPath func(repo string) (string, error)

	// WorktreeRoot is the parent directory under which each interactive
	// dispatch gets its own git worktree. Required.
	WorktreeRoot func() (string, error)

	// LogRoot is the parent directory for headless dispatch log files.
	// Required.
	LogRoot func() (string, error)

	// BinaryName is the host CLI's name, used only to format help text so
	// it reads correctly per consumer ("coily dispatch ..." vs
	BinaryName string

	// ClaudeConfigPath resolves the Claude Code config file holding
	// per-folder trust state. Optional; defaults to ~/.claude.json.
	ClaudeConfigPath func() (string, error)

	// Notify, when set, is called once per completed dispatch with a
	// summary Event. The consumer wires ntfy, a done banner, or any other
	Notify func(Event)

	// Seams below are pluggable so tests avoid spawning real processes or
	// shelling out to git. Production leaves them nil and New installs the
	SpawnDetached    func(repoPath, logPath, bin string, argv, env []string) (int, error)
	OpenLaunch       func(ctx context.Context, runner *shell.Runner, url string) error
	WorktreeAdd      func(ctx context.Context, runner *shell.Runner, repoPath, branch, worktreePath string) error
	WorktreeReapable func(ctx context.Context, runner *shell.Runner, repoPath, branch string) bool
	WorktreeRemove   func(ctx context.Context, runner *shell.Runner, repoPath, worktreePath, branch string) error
}

Config carries the host-specific seams the dispatch package refuses to hard-code. Required fields are noted; everything else has a documented

type Conflict

type Conflict struct {
	PID    int    `json:"pid"`
	Ref    string `json:"ref"`
	Claim  string `json:"claim"`
	Path   string `json:"path"`
	Reason string `json:"reason"`
}

Conflict is one (sidequest, claim) pair overlapping the query.

type Dispatcher

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

Dispatcher is a configured dispatch subsystem. Build one with New and hang Command off the host CLI's command tree.

func New

func New(cfg Config) (*Dispatcher, error)

New validates cfg, fills defaults, and returns a Dispatcher. It errors rather than panicking so the host CLI can fail loud at startup.

func (*Dispatcher) Command

func (d *Dispatcher) Command() *cli.Command

Command returns the dispatch umbrella verb. It refuses bare invocation and requires an explicit mode subverb (headless or interactive). The

type Event

type Event struct {
	Mode  string // "headless" or "interactive"
	Ref   string // owner/repo#N
	Title string
	URL   string
	Cwd   string
	PID   int // headless only; 0 otherwise
}

Event is the summary handed to Config.Notify when a dispatch completes.

type Issue

type Issue struct {
	Number int    `json:"number"`
	Title  string `json:"title"`
	Body   string `json:"body"`
	State  string `json:"state"`
	URL    string `json:"html_url"`
}

Issue is the platform-neutral fetch result. GitHub and Forgejo share the same JSON field names so one struct covers both.

type Platform

type Platform string

Platform tags which forge an issue ref resolves against.

const (
	PlatformGitHub  Platform = "github"
	PlatformForgejo Platform = "forgejo"
)

type Posture

type Posture string

Posture is the consult-posture axis: how readily a dispatched agent stops to involve the operator. Orthogonal to surface. coily#130.

const (
	// PostureHeadless never consults: complete the work end to end. The
	// posture the detached headless surface always runs under.
	PostureHeadless Posture = "headless"

	// PostureWatch proceeds in auto mode; operator may watch but is not
	// consulted. Historical interactive behavior, default for that surface.
	PostureWatch Posture = "watch"

	// PostureConsult proceeds but with a raised interruption budget:
	// encouraged to surface judgment calls. A soft expectation, not plan mode.
	PostureConsult Posture = "consult"

	// PostureCascade is the most autonomous posture: a detached worker
	// allowed to recursively dispatch headless/cascade sub-workers. coily#130.
	PostureCascade Posture = "cascade"
)

type Registry

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

Registry is the public read-only view over the sidequest surface. Downstream hosts use this rather than shelling out to each other.

func NewRegistry

func NewRegistry(logRoot string) *Registry

NewRegistry binds a Registry to a host-supplied LogRoot.

func (*Registry) Active

func (r *Registry) Active() ([]Sidequest, error)

Active returns every active sidequest (alive PID), oldest first.

func (*Registry) Conflicts

func (r *Registry) Conflicts(absPath string) ([]Conflict, error)

Conflicts returns every (sidequest, claim) pair overlapping absPath.

type Sidequest

type Sidequest struct {
	PID           int       `json:"pid"`
	Ref           string    `json:"ref"`
	URL           string    `json:"url,omitempty"`
	StartedAt     time.Time `json:"started_at"`
	ParentSession string    `json:"parent_session,omitempty"`
	PathsClaimed  []string  `json:"paths_claimed,omitempty"`
	LogPath       string    `json:"log_path"`
}

Sidequest is one active headless dispatch as exposed to consumers.

Jump to

Keyboard shortcuts

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