verb

package
v0.0.375 Latest Latest
Warning

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

Go to latest
Published: Jul 31, 2026 License: MIT Imports: 27 Imported by: 0

Documentation

Overview

Package verb is satelle's single execution path. Every data operation is a named function taking JSON in and returning JSON out, registered here. Both the CLI and (later) the local web server dispatch to the same Invoke functions — there is exactly one implementation per verb, so the two surfaces cannot drift.

This is the architecture's one seam: CLI command / web handler → verb.Dispatch → domain store → sqlite. The OSS tier is always local, so dispatch is always in-process. Stores are wired in as package globals (SetWorkItemStore, SetLedgerStore, SetDocIndexStore) at bootstrap.

Ported from satellites' internal/verb, stripped of the MCP role tier and auth wiring (no auth on the local surface).

Index

Constants

View Source
const (
	TopicStories = "stories"
	TopicTasks   = "tasks"
	TopicDocs    = "docs"
)

Realtime change topics — coarse, panel-level. A mutating verb publishes one after it commits so an open web page refetches just that panel.

View Source
const KindStoryDocAttached = "story_doc_attached"

KindStoryDocAttached records a document attachment on the story's ledger.

Variables

View Source
var ErrStoreNotConfigured = errors.New("verb: store not configured")

ErrStoreNotConfigured is returned when a verb runs before its backing store was wired. The in-process bootstrap calls the SetXStore functions below at startup; a nil store means a wiring bug, not a user error.

Functions

func AddChangeNotifier added in v0.0.279

func AddChangeNotifier(fn func(topic string))

AddChangeNotifier appends a sink on the shared change seam (e.g. the push publisher beside an existing SSE hub). Nil is ignored.

func AppendTelemetry added in v0.0.138

func AppendTelemetry(ctx context.Context, storyID, actor, kind string, data map[string]any) error

AppendTelemetry records one typed telemetry/quality event against a story — the generic write primitive (AC1) that also backs the dispatch engine's structured retry/failure/timeout capture (AC2) via agentstep.Engine.SetTelemetry. Best-effort at the ledger layer (nil store is a no-op, matching appendLedger), but VALIDATES data before writing — a caller-suppliable event must never carry a secret onto the ledger.

func AttachItemDoc added in v0.0.351

func AttachItemDoc(ctx context.Context, item workitem.Item, name, typ, body string) (string, string, error)

AttachItemDoc exposes the verb-owned typed attachment mechanism to the isolated-agent engine. The engine validates structured output first; this function writes it through the same path as `satelle story attach`.

func Catalog

func Catalog() []string

Catalog returns the names of every registered verb, sorted.

func ClearAgentsConfig added in v0.0.184

func ClearAgentsConfig()

ClearAgentsConfig resets the engage precondition wiring (tests).

func ClearTagVocabulary added in v0.0.249

func ClearTagVocabulary()

ClearTagVocabulary resets the tag vocabulary wiring (tests).

func CmpSemverExported added in v0.0.219

func CmpSemverExported(a, b string) int

CmpSemverExported is the package-external semver compare for drift gates.

func Dispatch

func Dispatch(ctx context.Context, name string, req json.RawMessage) (json.RawMessage, error)

Dispatch invokes a verb by name with raw JSON. Both CLI and web transports call here — same code path, same response shape.

func EventTelemetry added in v0.0.141

func EventTelemetry(e ledger.Entry) (agent, model, outcome string, tokensTotal int, durationMs int64)

EventTelemetry is the verb-package façade over ledger.EventTelemetry so story-cost and the web timeline share one reader (sty_43d228e4). Ownership of the extraction lives in ledger so the push-fed web package need not import verb.

func Register

func Register(v *Verb)

Register adds a verb to the global registry. Panics on duplicate names — verb names are a typed namespace and collisions are bugs.

func SetAgentsConfig added in v0.0.184

func SetAgentsConfig(agents config.AgentsConfig, vars map[string]string)

SetAgentsConfig wires the agents layer + [vars] KV for the engage precondition. Call with the result of requireAgents at CLI bootstrap. Pass zero values / omit the call in tests that do not need the check.

func SetAuthoredDirs added in v0.0.335

func SetAuthoredDirs(dirs map[string]string)

SetAuthoredDirs wires authored-markdown roots for the substrate change leg.

func SetBackupsDir added in v0.0.261

func SetBackupsDir(dir string)

SetBackupsDir wires the runtime backups root (<runtime>/backups).

func SetChangeNotifier added in v0.0.2

func SetChangeNotifier(fn func(topic string))

SetChangeNotifier replaces the notifier list with a single sink (or clears it when fn is nil). Prefer AddChangeNotifier when composing sinks.

func SetCreateReviewer added in v0.0.6

func SetCreateReviewer(r CreateReviewer)

SetCreateReviewer wires the required-structure reviewer. Pass nil to disable.

func SetDataDir added in v0.0.261

func SetDataDir(dir string)

SetDataDir wires the authored substrate root (toml, documents, workflows, …).

func SetDocIndexStore

func SetDocIndexStore(s *docindex.Store)

SetDocIndexStore wires the authored-doc index store.

func SetExecutorDispatcher added in v0.0.80

func SetExecutorDispatcher(d ExecutorDispatcher)

SetExecutorDispatcher wires the named-agent dispatch. Pass nil to disable.

func SetLeaseStore added in v0.0.211

func SetLeaseStore(s *lease.Store)

SetLeaseStore wires the engagement-lease store (sty_8426b9c0).

func SetLedgerStore

func SetLedgerStore(s *ledger.Store)

SetLedgerStore wires the evidence-ledger store.

func SetOpLog added in v0.0.19

func SetOpLog(l *oplog.Logger)

SetOpLog wires the flat-file operation log. Pass nil to disable (tests).

func SetRetrospector added in v0.0.133

func SetRetrospector(r Retrospector)

SetRetrospector wires the retrospective dispatcher. Pass nil to disable.

func SetStepSummariser added in v0.0.6

func SetStepSummariser(s StepSummariser)

SetStepSummariser wires the per-transition summariser. Pass nil to disable.

func SetStoryDir added in v0.0.6

func SetStoryDir(dir string)

SetStoryDir wires the directory that holds per-story attachments.

func SetStoryRetention added in v0.0.91

func SetStoryRetention(keepClosed, keepDays int)

SetStoryRetention wires the archive-retention policy for the per-story attachment dirs under .satelle/stories. Resolved from satelle.toml at app init.

func SetSubstrateConfigDir added in v0.0.336

func SetSubstrateConfigDir(dir string)

SetSubstrateConfigDir wires the resolved data dir (typically <repo>/.satelle) so non-kind config (agents.toml, constitution.md, hooks/) is enumerable.

func SetTagVocabulary added in v0.0.249

func SetTagVocabulary(cfg config.Config)

SetTagVocabulary wires the repo's tag vocabulary for create/set validation. Call with a.Config at CLI bootstrap. Pass zero / omit in tests that do not need the check.

func SetTaskDir added in v0.0.33

func SetTaskDir(dir string)

SetTaskDir wires the directory that holds task work-definition files.

func SetTransitionGater added in v0.0.6

func SetTransitionGater(g TransitionGater)

SetTransitionGater wires the reviewer that gates status transitions. Pass nil to disable gating (tests / no-reviewer environments).

func SetWorkItemStore

func SetWorkItemStore(s *workitem.Store)

SetWorkItemStore wires the stories/tasks store. Pass nil to reset (tests).

func SetWorkflowResolver added in v0.0.12

func SetWorkflowResolver(r WorkflowResolver)

SetWorkflowResolver wires the governing-workflow resolver. Pass nil to disable stamping.

func SyncStoryBacklog added in v0.0.44

func SyncStoryBacklog(ctx context.Context, store *workitem.Store, now time.Time) (materialized, pruned int, err error)

SyncStoryBacklog regenerates the read-only OKF backlog reference under the stories dir (.satelle/stories): every status:backlog story rendered as a generated concept file plus the reserved index.md/log.md. An engaged story (any non-backlog status) is NOT in the folder — the folder is the backlog. The SQLite store stays the SOLE story store (sty_fa1e02e1) — these files are a disposable, gitignored VIEW that no control logic reads (epic/parent close and the gates re-source from the DB); they exist only so the agent can browse the backlog on disk. Per-story attachment subdirs (<id>/) are left untouched, and the legacy pre-mirror-removal sty_*.md leftovers are cleaned up. Best-effort; a nil store or unset dir is a no-op. Returns the stories materialized and views pruned.

func SyncTasks added in v0.0.33

func SyncTasks(ctx context.Context, store *workitem.Store, now time.Time) (indexed, migrated int, err error)

SyncTasks reconciles the task files under taskDir with the store: any store task lacking a file is first ADOPTED by writing its file (migrating legacy DB-only tasks, e.g. tasks created before tasks became substrate), then every tsk_*.md is parsed and upserted so the FILE wins — it is the source of truth. Embedded default tasks (config.EmbeddedDefaults kind=tasks) with no on-disk file are ingested into the store WITHOUT writing a seed file (sty_29e5a9a5 virtual sparse defaults). Archived tasks are excluded from the store List that drives adoption, so an archived record is never resurrected (sty_cd209b8a). Returns (indexed, migrated). A no-op when taskDir is unset or the store is nil.

Types

type ChangelogEntry added in v0.0.218

type ChangelogEntry struct {
	Version  string              `json:"version"`
	Date     string              `json:"date,omitempty"`
	Breaking bool                `json:"breaking"`
	Sections map[string][]string `json:"sections,omitempty"`
	Raw      string              `json:"raw,omitempty"`
}

ChangelogEntry is one release section from CHANGELOG.md.

func ChangelogRange added in v0.0.219

func ChangelogRange(from, to string) ([]ChangelogEntry, error)

ChangelogRange returns entries with from < version <= to from CHANGELOG.md (on-disk preferred; embedded fallback). Used by the CLI drift gate.

type ChangelogResult added in v0.0.218

type ChangelogResult struct {
	From     string           `json:"from,omitempty"`
	To       string           `json:"to,omitempty"`
	Breaking bool             `json:"breaking"`
	Entries  []ChangelogEntry `json:"entries"`
}

ChangelogResult is the changelog verb response.

type CreateDraft added in v0.0.6

type CreateDraft struct {
	Kind               string   `json:"kind"`
	Title              string   `json:"title"`
	Body               string   `json:"body,omitempty"`
	AcceptanceCriteria string   `json:"acceptance_criteria,omitempty"`
	Priority           string   `json:"priority,omitempty"`
	Category           string   `json:"category,omitempty"`
	Tags               []string `json:"tags,omitempty"`
}

CreateDraft is a proposed work item handed to the required-structure reviewer before it is persisted.

type CreateReviewer added in v0.0.6

type CreateReviewer interface {
	ReviewCreate(ctx context.Context, draft CreateDraft) (GateDecision, error)
}

CreateReviewer judges a draft work item's required structure before creation, in an isolated subprocess. Implemented in internal/agentstep.

type DispatchResult added in v0.0.80

type DispatchResult struct {
	Dispatched bool   `json:"dispatched"`
	Agent      string `json:"agent,omitempty"`
	Command    string `json:"command,omitempty"`
	// Model is the binding's resolved model id ({model}), recorded on the
	// agent_invocation so the ledger shows WHICH model actually ran a step — the
	// audit signal for per-step model mixing (e.g. a GLM planner vs an opus
	// in-loop session, sty_5d48317b). The model id is not a secret; the binding's
	// env (endpoint + token) is deliberately NEVER recorded.
	Model string `json:"model,omitempty"`
	// Token/wall-time cost of the dispatch (sty_a699ad14), recorded on the
	// agent_invocation entry. Zero for a plain-text harness with no usage envelope.
	TokensIn    int    `json:"-"`
	TokensOut   int    `json:"-"`
	TokensTotal int    `json:"-"`
	DurationMs  int64  `json:"-"`
	Skill       string `json:"skill,omitempty"`
	// Output is the dispatched agent's captured stdout (sty_890b86cb). For a task
	// EXECUTION run, the verb layer writes it through as an OKF run-output document
	// under the parent task's folder, so a run's evidence is discoverable per task
	// rather than only in the central executor.log.
	Output string `json:"output,omitempty"`
	// ArtifactName/Type identify a Satelle-owned structured step artifact
	// attached before the transition committed.
	ArtifactName string `json:"artifact_name,omitempty"`
	ArtifactType string `json:"artifact_type,omitempty"`
}

DispatchResult reports a named-agent executor dispatch (sty_fd427546): whether the target state was allocated to a named isolated agent and, when it was, which agent/harness performed it and under which rubric.

type DocBody added in v0.0.274

type DocBody struct {
	Name string
	Type string
	Body string
}

DocBody is one attachment's name/type/body for payload injection (sty_58fa970e). Exported so agentstep can resolve docs without reaching into unexported storyDir.

func ItemDocs added in v0.0.274

func ItemDocs(ctx context.Context, id string) ([]DocBody, error)

ItemDocs returns every attachment for the item, name-sorted, with full bodies. Used by the isolated-agent payload docs injector so Bash-less reviewers can judge without any disk path. Empty slice (not error) when the dir is missing.

type ExecutorDispatcher added in v0.0.80

type ExecutorDispatcher interface {
	DispatchExecutor(ctx context.Context, item workitem.Item, toStatus string) (DispatchResult, error)
}

ExecutorDispatcher runs the named isolated agent a workflow node allocates a step to (agent=<name> — sty_fd427546): agents.toml defines WHO, the workflow DOT defines WHERE, the binary only RUNS it. Called after the edge's gates accept and BEFORE the status is enacted — an error refuses the transition (status unchanged). Implemented in internal/agentstep.

type GateDecision added in v0.0.6

type GateDecision struct {
	Gated     bool              // a reviewer skill judged this edge
	Accept    bool              // accept enacts the transition; reject blocks it
	Notes     string            // reviewer notes — pushback to the executor on reject
	Reasoning string            // optional free-form reasoning (verdict contract; may be empty)
	Skill     string            // the deciding reviewer skill
	Reviewers []ReviewerVerdict // per-reviewer verdicts in run order (empty for the legacy single-reviewer path)
	// Command/Context describe an isolated AGENT invocation (LLM reviewer): the
	// resolved harness command and the injected-context source (skill/rubric file).
	// Empty for a deterministic functional-check gate, which invokes no agent
	// (sty_fb3e0873). They mirror the deciding reviewer for the single-reviewer path.
	Command string
	Context string
	Model   string // the reviewer's resolved model id (sty_a699ad14) — for the cost view
	// TokensIn/Out/Total and DurationMs are the invocation's cost (sty_a699ad14),
	// recorded on the agent_invocation ledger entry so per-gate cost is auditable.
	// Zero for a functional-check gate or a plain-text harness that emits no usage.
	TokensIn    int
	TokensOut   int
	TokensTotal int
	DurationMs  int64
	// Unresolved names gate skills this edge DECLARED that do not resolve in the
	// substrate. Those gates degrade to advisory — the edge advances with no
	// reviewer and no verdict — which is deliberate, so a fresh repo works before
	// every gate is authored. It is EVIDENCE OF AN UNGATED ADVANCE, never a
	// verdict: an advance recorded with a non-empty Unresolved was not judged,
	// and without it that is indistinguishable from an edge carrying no gate at
	// all (sty_d59ec6a9).
	Unresolved []string
}

GateDecision is an isolated reviewer's verdict on a requested status transition. Gated reports whether a reviewer skill governed the edge at all — an ungated edge (no reviewer_skill, or its rubric not installed) is advisory and enacts directly, preserving the gateless baseline.

An edge may be judged by MORE THAN ONE reviewer: a transition can name an ordered list of reviewers, and an always-on system reviewer layer runs after them. Reviewers carries each reviewer's verdict in run order; the top-level Accept/Skill/Notes mirror the deciding reviewer (the first reject, or the last reviewer when all accept) so single-reviewer callers keep their contract.

type ProcessView added in v0.0.265

type ProcessView struct {
	Items       []substrate.Row                `json:"items"`
	Allocations []agentvalidate.GateAllocation `json:"allocations"`
	AgentsError string                         `json:"agents_error,omitempty"`
}

ProcessView is the effective-process payload for CLI/web (sty_ba0eb5c6).

type Retrospector added in v0.0.133

type Retrospector interface {
	Retrospect(ctx context.Context, item workitem.Item) (DispatchResult, error)
}

Retrospector dispatches the retrospective agent over a finished story to emit improvement proposals (sty_b53730e2). Implemented in internal/agentstep; verb holds only the seam.

type ReviewerVerdict added in v0.0.6

type ReviewerVerdict struct {
	Skill     string `json:"skill"`
	Order     int    `json:"order"`
	Accept    bool   `json:"accept"`
	Notes     string `json:"notes,omitempty"`
	Reasoning string `json:"reasoning,omitempty"` // optional free-form reasoning (verdict contract)
	System    bool   `json:"system,omitempty"`
	// Command/Context name the agent invocation behind an LLM reviewer's verdict —
	// the resolved harness command and the injected skill/rubric file — so the trail
	// records HOW it was judged, not just the outcome. Empty for a functional check.
	Command string `json:"command,omitempty"`
	Context string `json:"context,omitempty"`
	Model   string `json:"model,omitempty"` // reviewer's resolved model id (sty_a699ad14)
	// Token/wall-time cost of this reviewer's invocation (sty_a699ad14), recorded
	// on its agent_invocation entry for the per-gate cost view.
	TokensIn    int   `json:"tokens_in,omitempty"`
	TokensOut   int   `json:"tokens_out,omitempty"`
	TokensTotal int   `json:"tokens_total,omitempty"`
	DurationMs  int64 `json:"duration_ms,omitempty"`
}

ReviewerVerdict is one reviewer's verdict within a transition's ordered review. Order is its position in the run (workflow-named reviewers first, then the always-on system layer); System marks a verdict from that layer.

type StepSummariser added in v0.0.6

type StepSummariser interface {
	Summarise(ctx context.Context, item workitem.Item, from, to string) (SummaryResult, error)
	// MandatorySummary reports whether item's active workflow declares a MANDATORY
	// step-summary node — the gate for surfacing a missing-summary gap at done
	// (sty_a1151fb0).
	MandatorySummary(ctx context.Context, item workitem.Item) bool
}

StepSummariser produces a read-only prose recap of an enacted transition, recorded as a step_summary ledger row. Implemented in internal/agentstep.

type StoryCost added in v0.0.132

type StoryCost struct {
	StoryID         string         `json:"story_id"`
	Rows            []StoryCostRow `json:"rows"`
	Steps           []StoryStepRow `json:"steps,omitempty"`
	TotalTokens     int            `json:"total_tokens"`
	TotalDurationMs int64          `json:"total_duration_ms"`
	TotalWallMs     int64          `json:"total_wall_ms,omitempty"`
}

StoryCost is the per-story rollup: the dispatched/reviewed invocations (Rows, the precise sub-process cost) AND the per-step report (Steps, every state's wall-time plus any self-reported in-loop tokens / per-step estimate). Together they make the full cost of a driven story legible — dispatched and in-loop (sty_a699ad14, sty_3b2e55f5).

func ComputeStoryCost added in v0.0.132

func ComputeStoryCost(ctx context.Context, storyID string) (StoryCost, error)

ComputeStoryCost reads the story's ledger and builds two complementary views:

  • Rows: the dispatched/reviewer agent_invocation cost (precise tokens + agent wall-time), exactly as before — entries with no usage contribute zero.
  • Steps: a per-step report. Each state's WALL-TIME is derived from the deltas between consecutive status_transition timestamps (so IN-LOOP steps, which spawn no measurable subprocess, still get a duration), and its self-reported actual tokens + per-step estimate are merged from any step_cost entry.

Rows stay oldest-first, matching the ledger order; Steps follow first-occurrence order across the lifecycle.

type StoryCostRow added in v0.0.132

type StoryCostRow struct {
	From        string `json:"from"`
	To          string `json:"to"`
	Agent       string `json:"agent"`
	Skill       string `json:"skill,omitempty"`
	Model       string `json:"model,omitempty"`
	TokensIn    int    `json:"tokens_in"`
	TokensOut   int    `json:"tokens_out"`
	TokensTotal int    `json:"tokens_total"`
	DurationMs  int64  `json:"duration_ms"`
}

StoryCostRow is one dispatched/reviewed step's recorded cost — the per-gate tokens + wall-time captured on an agent_invocation ledger entry (sty_a699ad14).

type StoryStepRow added in v0.0.135

type StoryStepRow struct {
	Step          string `json:"step"`
	WallTimeMs    int64  `json:"wall_time_ms"`
	TokensTotal   int    `json:"tokens_total,omitempty"` // self-reported in-loop actual
	HasTokens     bool   `json:"has_tokens,omitempty"`   // false = unrecorded (a subprocess can't measure it), NOT free
	EstTokens     int    `json:"est_tokens,omitempty"`
	EstDurationMs int64  `json:"est_duration_ms,omitempty"`
}

StoryStepRow is one workflow STEP's cost report: the wall-time the story spent in that state (derived from the transition timestamps — this is how an IN-LOOP step, whose tokens a subprocess can't measure, still gets a recorded cost) plus, where `satelle story step-cost` recorded them, the step's self-reported actual tokens and its per-step estimate. This is the per-step est-vs-actual view (sty_3b2e55f5).

type StorySyncReport added in v0.0.70

type StorySyncReport struct {
	Materialized int      `json:"materialized"`       // backlog views written/refreshed
	Pruned       int      `json:"pruned"`             // stale/non-backlog views removed
	ArtifactDirs int      `json:"artifact_dirs"`      // <sty_id>/ dirs present
	Orphaned     []string `json:"orphaned,omitempty"` // artifact dirs with no DB story
	Problems     []string `json:"problems,omitempty"` // artifact frontmatter mismatches
}

StorySyncReport summarises one full .satelle/stories reconciliation (sty_8f7b2157): the view counts plus the artifact review — orphaned artifact dirs (story id absent from the DB) and artifact files whose frontmatter contradicts their location are REPORTED, never deleted (artifacts are authored evidence; the operator decides).

func SyncStories added in v0.0.6

func SyncStories(ctx context.Context, store *workitem.Store, now time.Time) (StorySyncReport, error)

SyncStories runs the full stories-dir reconciliation and REVIEWS the artifacts (sty_8f7b2157): the shared SyncStoryBacklog core (materialize the backlog-only views, prune stale, migrate legacy summaries) plus a review of every <sty_id>/ artifact dir against the database. The dedicated `satelle story sync` verb calls this; reindex/serve keep calling the core.

type SummaryResult added in v0.0.138

type SummaryResult struct {
	Text        string
	Command     string
	Context     string
	Model       string
	TokensIn    int
	TokensOut   int
	TokensTotal int
	DurationMs  int64
}

SummaryResult is the step summariser's read-only recap plus the cost of the isolated agent invocation that produced it (sty_a699ad14): Command/Context/ Model/tokens/duration mirror a reviewer's ReviewerVerdict so the same agent_invocation payload shape covers both. Zero-value cost fields mean the summariser produced no billable invocation (e.g. it never ran).

type TransitionGater added in v0.0.6

type TransitionGater interface {
	Gate(ctx context.Context, item workitem.Item, toStatus string) (GateDecision, error)
}

TransitionGater judges a requested status transition in an isolated, fresh-context subprocess. The implementation lives in internal/agentstep; verb holds only the seam so the dispatch layer stays free of the agent CLI.

type Verb

type Verb struct {
	Name        string
	Description string
	Invoke      func(ctx context.Context, req json.RawMessage) (json.RawMessage, error)
}

Verb is a named entry in the dispatch registry.

func Get

func Get(name string) *Verb

Get returns the verb registered under name, or nil.

type VersionInfo

type VersionInfo struct {
	Version   string `json:"version"`
	Commit    string `json:"commit"`
	BuildTime string `json:"build_time"`
}

VersionInfo is the response shape for the version verb. It mirrors buildinfo.Info; kept as the verb's own type so the wire shape is stable independent of the buildinfo internals.

type WorkflowResolver added in v0.0.12

type WorkflowResolver interface {
	WorkflowNameFor(ctx context.Context, category string) string
	// WorkflowStates returns the lifecycle states the named workflow declares and
	// whether the workflow resolves at all — the restamp validation seam. An empty
	// state list on a resolved workflow means the lifecycle was not statically
	// parseable; the caller skips the status-compatibility check rather than
	// blocking the restamp.
	WorkflowStates(ctx context.Context, name string) ([]string, bool)
}

WorkflowResolver names the workflow that governs a story of a given category, so the create path can STAMP the choice on the story (sty_3800ac23) and the restamp path can re-resolve it mid-flight (sty_ed3386cf). Wired independently of create-gating — a story is stamped whenever a workflow governs it. Implemented in internal/agentstep.

Jump to

Keyboard shortcuts

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