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 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 ¶
NewRegistry binds a Registry to a host-supplied LogRoot.
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.