Documentation
¶
Overview ¶
Package fleet turns an operator's plan into a working agent team.
The generating model NEVER writes files. It proposes a manifest; this package validates it and writes the tree. Two reasons that split matters:
- `.claude/` is a protected path. Writes there are not auto-approved in default or acceptEdits mode and are denied outright in dontAsk, and `permissions.allow` does not pre-approve them. Making it work would mean granting bypassPermissions to a session that just ingested an untrusted operator plan.
- Writing from Go makes the namespace contract mechanical: every generated file is recorded, so a later regeneration can tell its own output from something a human edited.
Index ¶
Constants ¶
const ( // MaxPlanIssues caps how many issues one plan may spawn. A vague plan can // decompose into fifty items, which is fifty body sessions and a board // full of half-guessed work — a bill and a backlog nobody agreed to. The // cap is exported so the wizard can name it when reporting that it bit; // silent truncation would leave the operator believing the plan produced // exactly what they see. MaxPlanIssues = 15 )
const RosterBudget = rosterTimeout + 5*time.Minute
RosterBudget is how long a caller should allow phase 1 before concluding it hung: the session's own cap plus margin for spawn and parse.
Variables ¶
This section is empty.
Functions ¶
func PhaseBudget ¶ added in v0.1.1
PhaseBudget is how long a caller should allow the write phase given how many items REMAIN. It budgets remaining work, not the whole run: the wizard re-arms its watchdog with this on every completion, so a big fleet earns time by making progress while a hung one still dies within a single wave.
Types ¶
type AgentSpec ¶
type AgentSpec struct {
Slug string `json:"slug"`
DisplayName string `json:"display_name"`
// Description is the delegation trigger — it is what the router matches on,
// so a vague one makes the agent unreachable.
Description string `json:"description"`
Role string `json:"role"` // orchestrator | builder | advisor | reviewer
Model string `json:"model"` // haiku | sonnet | opus | inherit
Areas []string `json:"areas"`
Tools []string `json:"tools"`
Skills []string `json:"skills"`
Persona string `json:"persona"` // the system prompt body, markdown
}
type AgentsService ¶
type AgentsService struct {
// contains filtered or unexported fields
}
func NewAgentsService ¶
func NewAgentsService(db *sql.DB, log *slog.Logger) *AgentsService
func (*AgentsService) Routes ¶
func (s *AgentsService) Routes(r chi.Router)
type Generator ¶
type Generator struct {
// contains filtered or unexported fields
}
Generator turns a plan into a fleet.
func (*Generator) Generate ¶
func (g *Generator) Generate(ctx context.Context, fleetName, plan, model string, onProgress Progress) (*Manifest, float64, error)
Generate runs both phases, writes the tree and persists the fleet.
Durability contract: the roster is persisted the moment it is decided, and every persona and skill body the moment its session returns. A run that dies at agent 20 of 27 leaves 20 personas in the database, and the next call with the same plan resumes at agent 21 — it re-runs neither the roster session (re-deciding could produce a different team than the personas already written) nor anything already on file. Pressing Generate twice is free.
func (*Generator) GenerateIssues ¶ added in v0.1.1
func (g *Generator) GenerateIssues(ctx context.Context, fleetName, plan string, onProgress Progress) (created, dropped int, spent float64, err error)
GenerateIssues breaks the plan into board issues assigned to the fleet.
Durability contract, same as Generate: the decided list is persisted as skeleton rows the moment phase 1 returns, and each body the moment its session returns. A re-run with the same plan re-buys nothing that is already on file — pressing the button twice is free, and a run that died partway resumes at the first missing body.
Every issue lands human_only = true and source = 'import': plan-derived work should be read by a person before agents spend on it, and the board must be able to say which issues a plan produced versus what a human filed. Issues whose area matches an agent's declared areas land 'ready', assigned; an area nobody owns lands in 'triage', unassigned — handing it to whoever looks nearest is how an agent ends up editing a surface it does not own.
Returns how many issues the plan produced on the board and how many the cap dropped; dropped > 0 must reach the operator, not just the log.
type Manifest ¶
type Manifest struct {
Summary string `json:"summary"`
Agents []AgentSpec `json:"agents"`
Skills []SkillSpec `json:"skills"`
Notes []string `json:"notes,omitempty"`
}
Manifest is what the generator returns.
func (*Manifest) Validate ¶
Validate normalizes the manifest and reports every problem at once, so a regeneration prompt can be given the full list rather than one error per round.
func (*Manifest) Write ¶
func (m *Manifest) Write(root string) ([]WrittenFile, error)
Write renders the manifest into `<root>/.claude/`.
Generated files land in their own namespace and are recorded in manifest.json, so a later run can distinguish its own previous output from a file a human has since edited — and never clobber the latter.
type Progress ¶
Progress is reported back to the wizard as the phases advance, so a run that takes ten minutes does not look like a hang.
spentUSD is included because a ten-minute run showing $0.00 throughout gives the operator no way to tell a cheap run from an expensive one until it is over — by which point the money is spent.
type WrittenFile ¶
type WrittenFile struct {
Path string `json:"path"`
SHA256 string `json:"sha256"`
Owner string `json:"owner"`
}
WrittenFile records one file the generator produced.