Documentation
¶
Overview ¶
Package server wires the HTTP surface: chi router, SPA fallback, JSON API, and the SSE hub. NewRouter accepts the embedded SPA filesystem as a parameter so this package has no compile-time dependency on the embed declaration in cmd/gemba.
For v0 of the scaffold, handlers return placeholder 501 responses. The real surface lands with gm-e2.6.
Package server wires the HTTP surface. See doc.go for the package-level overview.
/api/spec-kit/* exposes Spec Kit planning artifacts as a first-class refinement source and syncs approved artifacts into Beads.
Index ¶
- Constants
- Variables
- func NewPoolWiring(op core.OrchestrationPlaneAdaptor, wp core.WorkPlane, pool config.ResolvedPool, ...) *poolWiring
- func OpenAPISpec() []byte
- func SetGTRunner(r gtRunner)
- type AdoptableLister
- type AttachConfig
- type BeadsPinger
- type BindProjectRequest
- type BootstrapAnalysis
- type BootstrapAnalysisSeed
- type BootstrapCommitResult
- type BootstrapFinding
- type BootstrapPlanItem
- type BootstrapProgressFrame
- type BootstrapSource
- type BootstrapStore
- type ChangeRef
- type CommandRunner
- type DraftBead
- type DraftEpic
- type DraftMilestone
- type FilesystemBackend
- type NewProjectRatifier
- type NewProjectSession
- type NewProjectState
- type NewProjectStore
- type NonceCache
- type OrchestrationPC
- type OrchestrationSc
- type OrchestrationState
- type PersonaFile
- type PerspectiveContribution
- type PerspectiveContributionKind
- type PoolConfigEnvelope
- type PoolMember
- type PoolState
- type PoolStateEntry
- type PoolWiring
- type RatifierConfig
- type RatifyError
- type RatifyResponse
- type RecycleWriter
- type Router
- func (r *Router) ActiveProject() string
- func (r *Router) AttachBootstrap(store BootstrapStore)
- func (r *Router) AttachMetricsHTTPClient(c *http.Client)
- func (r *Router) AttachMetricsHandler(h http.Handler)
- func (r *Router) AttachNewProject(store NewProjectStore, turner SkillTurner, ratifier NewProjectRatifier)
- func (r *Router) AttachPersonaDispatcher(d *persona.Dispatcher, sr *corepersona.SkillRegistry, pr *corepersona.Registry)
- func (r *Router) AttachPersonasDir(dir string)
- func (r *Router) AttachPhase(store phase.Store)
- func (r *Router) AttachPoolConfig(path string, maxParallel, reservedForManual int)
- func (r *Router) AttachPools(resolved []config.ResolvedPool)
- func (r *Router) AttachProjects(cfg AttachConfig)
- func (r *Router) AttachWalk(store walk.Store, sources walk.Sources)
- func (r *Router) AttachWalkSummary(s *walk_summary.Skill)
- func (r *Router) AttachWorkflowClient(c *workflow.Client)
- func (r *Router) Close()
- func (r *Router) EventsHub() *events.Hub
- func (r *Router) HealthBus() *registry.HealthBus
- func (r *Router) Host() *api.Host
- func (r *Router) InstanceID() string
- func (r *Router) PhaseStore() phase.Store
- func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)
- func (r *Router) StartHealthBus()
- type SkillTurner
Constants ¶
const ConfirmHeader = "X-GEMBA-Confirm"
ConfirmHeader is the request header every mutating route requires. Echo it back in the response so callers can confirm the server saw the right token (helps debug "did my retry land?" without parsing bodies).
Variables ¶
var ErrBootstrapNotFound = errors.New("bootstrap: analysis not found")
ErrBootstrapNotFound is returned by BootstrapStore.Get / Commit when the analysis id is unknown.
var ErrNewProjectNotFound = errors.New("newproject: session not found")
ErrNewProjectNotFound is returned by NewProjectStore when the session id is unknown.
Functions ¶
func NewPoolWiring ¶
func NewPoolWiring( op core.OrchestrationPlaneAdaptor, wp core.WorkPlane, pool config.ResolvedPool, cfg config.PoolConfig, readers planner.OperationalContextReaders, agentType string, ) *poolWiring
NewPoolWiring constructs a PoolWiring bound to one (rig, persona) pool. The returned struct's methods plug into autodispatch.Daemon's interface fields. agentType is derived from the persona's TOML configuration upstream and threaded into the SessionDispatcher's prompt extension as `gemba:agent_type` so the native adaptor's pane-reuse path can find a slot.
func OpenAPISpec ¶
func OpenAPISpec() []byte
OpenAPISpec returns the embedded OpenAPI document bytes. Exported so cmd/gen-core-types and offline tooling can pull the same source the runtime serves without spinning up the server.
func SetGTRunner ¶
func SetGTRunner(r gtRunner)
SetGTRunner overrides the gt-shellout runner used by the orchestration mutation handlers. Tests use this to inject a fake without spawning processes. Pass nil to restore the default.
Types ¶
type AdoptableLister ¶
AdoptableLister enumerates adoptable beads DBs. Production impl connects to the configured Dolt server. Tests substitute a stub via AttachProjects(AttachConfig{AdoptableLister: ...}) so they don't need a real Dolt server on port 3307.
type AttachConfig ¶
type AttachConfig struct {
Pinger BeadsPinger
GitInitRunner CommandRunner
Now func() time.Time
// AdoptableLister backs GET /api/v1/projects/adoptable (gm-gmyl).
// Nil falls back to the production lister that opens a mysql
// connection against the configured Dolt server. Tests inject a
// stub here.
AdoptableLister AdoptableLister
}
AttachConfig configures the attach handler. Zero values are fine — Pinger nil falls back to defaultBeadsPinger, GitInitRunner nil falls back to exec.
type BeadsPinger ¶
BeadsPinger verifies a beads DB URL is reachable. The production implementation opens a mysql connection via the go-sql-driver, pings, and closes. Tests substitute a stub so they don't need a real Dolt server on port 3307.
type BindProjectRequest ¶
type BindProjectRequest struct {
// BeadsDBPath is the absolute path to the directory holding the
// beads DB (the parent of the `.beads/` subdir). The handler
// verifies the directory has a `.beads/` child before doing any
// work.
BeadsDBPath string `json:"beads_db_path"`
// TargetRepoPath is the absolute path to the directory that will
// hold the final project. For mode == "create" this MUST equal
// BeadsDBPath. For mode == "navigate" this MUST be a pre-existing
// git working tree.
TargetRepoPath string `json:"target_repo_path"`
// Mode is "create" or "navigate" — see the file header.
Mode string `json:"mode"`
}
BindProjectRequest is the request body for POST /api/v1/projects/bind.
type BootstrapAnalysis ¶
type BootstrapAnalysis struct {
ID string `json:"analysis_id"`
Source BootstrapSource `json:"source"`
Config map[string]any `json:"config,omitempty"`
Frames []BootstrapProgressFrame `json:"-"`
Plan []BootstrapPlanItem `json:"-"`
Findings []BootstrapFinding `json:"-"`
StartedAt time.Time `json:"started_at"`
CompletedAt time.Time `json:"completed_at,omitempty"`
Committed bool `json:"committed"`
}
BootstrapAnalysis is the persisted record for one in-flight analysis. The store mints id; AppendProgress drives the long-poll stream; Plan + Findings are populated on completion; Committed flips on a successful POST /commit (idempotent guard against a second commit).
type BootstrapAnalysisSeed ¶
type BootstrapAnalysisSeed struct {
Frames []BootstrapProgressFrame
Plan []BootstrapPlanItem
Findings []BootstrapFinding
}
BootstrapAnalysisSeed bundles the canned analysis output the handler synthesises off the source. The store doesn't care what the values mean — it just persists them so subsequent Get/Frames/ Plan calls can read them back.
type BootstrapCommitResult ¶
type BootstrapCommitResult struct {
ProjectPath string `json:"project_path"`
BoardURL string `json:"board_url"`
}
BootstrapCommitResult is the response shape /commit hands back. project_path lands in the success toast; board_url is the SPA's post-ratify destination.
type BootstrapFinding ¶
type BootstrapFinding struct {
ID string `json:"id"`
Level string `json:"level"` // pass | warn | fail
Title string `json:"title"`
Detail string `json:"detail,omitempty"`
}
BootstrapFinding is one row in the consistency report. level is pass | warn | fail; the SPA totals the levels for the top-line PASS=12 / WARN=2 / FAIL=0 summary.
type BootstrapPlanItem ¶
type BootstrapPlanItem struct {
ID string `json:"id"`
Kind string `json:"kind"` // epic | milestone | sprint | story
Title string `json:"title"`
Detail string `json:"detail,omitempty"`
Children []BootstrapPlanItem `json:"children,omitempty"`
}
BootstrapPlanItem is one outline row in the generated plan (epic / milestone / sprint / story). Children render the two-level outline the SPA expects.
type BootstrapProgressFrame ¶
type BootstrapProgressFrame struct {
Seq int `json:"seq"`
Line string `json:"line"`
At time.Time `json:"at"`
Done bool `json:"done,omitempty"`
}
BootstrapProgressFrame is one streamed line of analysis progress. JSON shape mirrors web/src/api/bootstrap.ts BootstrapProgress.
type BootstrapSource ¶
type BootstrapSource string
BootstrapSource enumerates the four source kinds the wizard supports per ui-spec §5.15. Mirrors the typed client's union exactly so the JSON token round-trips without translation.
const ( BootstrapSourceJira BootstrapSource = "jira" BootstrapSourceBeads BootstrapSource = "beads" BootstrapSourceSourceCode BootstrapSource = "source-code" BootstrapSourceFresh BootstrapSource = "fresh" )
type BootstrapStore ¶
type BootstrapStore interface {
// Start allocates a new analysis with frames seeded from
// `seed` (the synthesised progress sequence keyed off source).
// Returns the persisted record so the handler can read the
// minted id back without a second Get.
Start(ctx context.Context, source BootstrapSource, config map[string]any, seed BootstrapAnalysisSeed) (BootstrapAnalysis, error)
// Get returns the analysis by id. ErrBootstrapNotFound when
// unknown.
Get(ctx context.Context, id string) (BootstrapAnalysis, error)
// FramesSince returns frames with seq > since, in seq order.
// Used by GET /progress for the long-poll loop.
FramesSince(ctx context.Context, id string, since int) ([]BootstrapProgressFrame, error)
// Commit marks the analysis committed and returns the resulting
// (project_path, board_url) pair. Idempotent: a second commit
// returns the same response; ErrBootstrapAlreadyCommitted is
// surfaced when the caller wants to know they raced (the HTTP
// handler treats that as success).
Commit(ctx context.Context, id string) (BootstrapCommitResult, error)
}
BootstrapStore is the persistence interface. All methods are context-aware so a future SQL implementation honours deadlines. Mirrors walk.Store's narrow shape: callers hand pre-built records to mutation methods, the store concerns itself only with id + lifecycle.
func NewMemoryBootstrapStore ¶
func NewMemoryBootstrapStore() BootstrapStore
NewMemoryBootstrapStore returns a fresh in-memory store. cmd/gemba serve calls this once at boot and hands the result to AttachBootstrap.
type ChangeRef ¶
type ChangeRef = newproject.ChangeRef
ChangeRef points at the most-recent skill edit.
type CommandRunner ¶
CommandRunner is the shell-out abstraction the ratifier uses. The production implementation forwards to exec.CommandContext; tests substitute a recording stub so they can drive failures deterministically without touching the host's bd / git binaries.
type DraftBead ¶
type DraftBead = newproject.DraftBead
DraftBead is the leaf of the plan tree. See internal/skills/newproject for the field-level documentation; the JSON wire shape is canonical at that location.
type DraftMilestone ¶
type DraftMilestone = newproject.DraftMilestone
DraftMilestone is one milestone in the plan tree.
type FilesystemBackend ¶
type FilesystemBackend interface {
Stat(path string) (os.FileInfo, error)
MkdirAll(path string, perm os.FileMode) error
WriteFile(path string, data []byte, perm os.FileMode) error
RemoveAll(path string) error
}
FilesystemBackend is the file-write abstraction. Production wires it to os.MkdirAll / os.WriteFile / os.RemoveAll; tests substitute a stub that maps writes onto an in-memory tree (or a tmpfs). Splitting it out lets us test step-by-step rollback without spawning subprocesses.
type NewProjectRatifier ¶
type NewProjectRatifier interface {
Ratify(ctx context.Context, state NewProjectState) (RatifyResponse, error)
}
NewProjectRatifier executes the atomic ratification transaction. Returns the SPA's success envelope on commit, or a structured RatifyError on any step failure.
func NewRatifier ¶
func NewRatifier(cfg RatifierConfig) NewProjectRatifier
NewRatifier builds a production NewProjectRatifier. cmd/gemba serve calls this once at boot and hands the result to AttachNewProject.
type NewProjectSession ¶
type NewProjectSession struct {
ID string
State NewProjectState
StartedAt time.Time
}
NewProjectSession is the in-memory record for one /onboard conversation. Lives only in server process memory — refresh / restart / Ctrl-C discards it (one-shot persistence is by design; see docs/design/newproject.md §One-shot persistence).
type NewProjectState ¶
type NewProjectState = newproject.NewProjectState
NewProjectState is the authoritative state for one /api/v1/newproject conversation session.
type NewProjectStore ¶
type NewProjectStore interface {
// Start allocates a new session with the empty state and returns
// the persisted record so the handler can read the minted id back
// without a second Get.
Start(ctx context.Context) (NewProjectSession, error)
// Get returns the session by id. ErrNewProjectNotFound when
// unknown.
Get(ctx context.Context, id string) (NewProjectSession, error)
// Update replaces the state of an existing session. Returns
// ErrNewProjectNotFound if the id is unknown.
Update(ctx context.Context, id string, state NewProjectState) error
// Delete drops the session from the store. Used after a
// successful ratify to free memory; idempotent — deleting an
// unknown id is not an error.
Delete(ctx context.Context, id string)
}
NewProjectStore is the persistence interface for /onboard conversation sessions. Mirrors the bootstrap / walk store pattern: callers hand pre-built records to mutation methods, the store concerns itself only with id + lifecycle.
func NewMemoryNewProjectStore ¶
func NewMemoryNewProjectStore() NewProjectStore
NewMemoryNewProjectStore returns a fresh in-memory store. cmd/gemba serve calls this once at boot and hands the result to AttachNewProject.
type NonceCache ¶
type NonceCache struct {
// contains filtered or unexported fields
}
NonceCache caches recent (nonce → response) tuples so a replayed mutating request returns the cached response unchanged.
Capacity bounds memory; TTL keeps stale entries from sitting forever once the LRU stops touching them.
func NewNonceCache ¶
func NewNonceCache(capacity int, ttl time.Duration) *NonceCache
NewNonceCache returns a cache with the given capacity (max entries) and TTL. Defaults: capacity=1024, ttl=5min — sized for normal SPA double-click protection without a real footprint cost.
func (*NonceCache) Get ¶
func (c *NonceCache) Get(nonce string) (cachedResponse, bool)
Get returns the cached response for nonce when present and not expired. Touches LRU order so a hot nonce stays warm.
func (*NonceCache) Put ¶
func (c *NonceCache) Put(nonce string, resp cachedResponse)
Put stores resp under nonce, evicting the least-recently-used entry if the cache is at capacity.
type OrchestrationPC ¶
type OrchestrationPC struct {
Scope string `json:"scope"`
Persona string `json:"persona"`
Name string `json:"name"`
State string `json:"state,omitempty"`
}
OrchestrationPC — one polecat row, gt-only. Empty on native.
type OrchestrationSc ¶
type OrchestrationSc struct {
ID string `json:"id"`
Kind string `json:"kind"` // "rig" | "local"
Personas []string `json:"personas"`
}
OrchestrationSc — one scope row. For native this is the single implicit local scope ("local"). For gt this is one rig.
type OrchestrationState ¶
type OrchestrationState struct {
AdaptorID string `json:"adaptor_id"`
Scopes []OrchestrationSc `json:"scopes"`
AgentTypes []string `json:"agent_types"`
Polecats []OrchestrationPC `json:"polecats,omitempty"`
}
OrchestrationState is the wire shape /api/orchestration/state returns. Mirrors web/src/api/orchestrationState.ts.
type PersonaFile ¶
type PersonaFile struct {
ID string `json:"id"` // filename stem
Name string `json:"name"` // [persona] name (top-level "name" key)
AgentType string `json:"agent_type"` // declared agent_type if present
Skills []string `json:"skills,omitempty"`
}
PersonaFile is one row in the response.
type PerspectiveContribution ¶
type PerspectiveContribution struct {
PersonaID string `json:"persona_id"`
PersonaName string `json:"persona_name,omitempty"`
PersonaIcon string `json:"persona_icon,omitempty"`
Kind PerspectiveContributionKind `json:"kind"`
Content string `json:"content"`
At time.Time `json:"at"`
}
PerspectiveContribution is one persona's volunteered comment on an active agenda item. Stable JSON shape — additive fields can land later without breaking the SPA.
type PerspectiveContributionKind ¶
type PerspectiveContributionKind string
PerspectiveContributionKind classifies an inline perspective. Kept as a typed string so the SPA can branch on it for styling (blocking → red, amendment → amber, note → neutral) without parsing the human-readable content.
const ( PerspectiveKindBlocking PerspectiveContributionKind = "blocking" PerspectiveKindAmendment PerspectiveContributionKind = "amendment" PerspectiveKindNote PerspectiveContributionKind = "note" )
type PoolConfigEnvelope ¶
type PoolConfigEnvelope struct {
Path string `json:"path"`
Body string `json:"body"`
Parsed poolConfigJSON `json:"parsed"`
MaxParallel int `json:"max_parallel"`
ReservedForManual int `json:"reserved_for_manual"`
}
PoolConfigEnvelope is the wire shape both GET and PUT return. Mirrors web/src/api/poolConfig.ts.
type PoolMember ¶
type PoolMember struct {
SessionID string `json:"session_id"`
PaneID string `json:"pane_id,omitempty"`
Status string `json:"status"`
LastBead string `json:"last_bead,omitempty"`
BeadsDoneThisMember int `json:"beads_done_this_member,omitempty"`
LastRecycleAt *time.Time `json:"last_recycle_at,omitempty"`
}
PoolMember is the wire shape of one pool slot's row.
type PoolState ¶
type PoolState struct {
Pools []PoolStateEntry `json:"pools"`
CapturedAt time.Time `json:"captured_at"`
}
PoolState is the response envelope for GET /api/pools.
type PoolStateEntry ¶
type PoolStateEntry struct {
Scope string `json:"scope"`
Persona string `json:"persona"`
SizeTargetDeclared int `json:"size_target_declared"`
SizeTargetEffective int `json:"size_target_effective"`
SizeActual int `json:"size_actual"`
Idle int `json:"idle"`
Working int `json:"working"`
Members []PoolMember `json:"members"`
}
PoolStateEntry is one (scope, persona) pool's exposed state.
gm-s47n.16 (spec §2): the JSON field renamed `rig` → `scope`. Existing SPA consumers reading `rig` are updated; the change ships in lockstep with the editor.
type PoolWiring ¶
type PoolWiring = poolWiring
PoolWiring is the public alias the cli package uses to construct a daemon's adaptors. Produced by NewPoolWiring.
type RatifierConfig ¶
type RatifierConfig struct {
// HomeDir overrides $HOME for default-dir resolution. Tests set
// this so the transaction lands inside a t.TempDir() instead of
// the developer's real home.
HomeDir string
// DefaultDirOverride bypasses the config-file lookup entirely.
// Tests use this to force the projects directory without faking
// out the home-dir filesystem.
DefaultDirOverride string
// Runner shells external commands (bd, git). nil → exec-based
// default.
Runner CommandRunner
// FS abstracts filesystem writes. nil → osFS.
FS FilesystemBackend
// Now returns the current time; tests pin this for deterministic
// timestamps in workspace.toml.
Now func() time.Time
}
RatifierConfig configures the production ratifier. Zero values are fine — DefaultDir empty falls back to ~/.gemba/config.toml resolution, Runner empty falls back to exec, FS empty falls back to osFS.
type RatifyError ¶
type RatifyError struct {
// Step is the 1-based transaction step that failed (1..10, or
// "8a" for the dep-resolution sub-step which we encode as 80).
Step int
// Code is a short machine-readable token (validation_failed,
// dir_exists, git_init_failed, beads_init_failed,
// milestone_create_failed, epic_create_failed, bead_create_failed,
// dep_resolve_failed, cycle_detected, project_md_failed,
// commit_failed, …).
Code string
// Message is the human-readable diagnostic surfaced in the toast.
Message string
// Cause is the underlying error, retained for logging. May be nil
// when the step itself synthesised the error.
Cause error
}
RatifyError is the structured error every step of the transaction returns on failure. The HTTP layer maps Step + Code into the JSON envelope so the SPA can render a precise diagnostic.
func (*RatifyError) Error ¶
func (e *RatifyError) Error() string
func (*RatifyError) Unwrap ¶
func (e *RatifyError) Unwrap() error
type RatifyResponse ¶
type RatifyResponse struct {
ProjectPath string `json:"project_path"`
ProjectName string `json:"project_name"`
MilestoneCount int `json:"milestone_count"`
EpicCount int `json:"epic_count"`
// SeedWarnings carries any non-fatal failures from the FTUX seed
// steps (.gemba/agents.toml, .gemba/personas/, CLAUDE.md — gm-root.24).
// These are best-effort additions: a failure here does NOT roll back
// the project — the SPA renders a "partial seed" toast so the
// operator can hand-fix and proceed. Empty (or omitted) means every
// seed step succeeded.
SeedWarnings []string `json:"seed_warnings,omitempty"`
}
RatifyResponse is the success envelope POST /ratify returns. The SPA owns post-ratify navigation (gm-root.17.7); the server returns the new project's filesystem root, its display name, and the seeded milestone / epic counts so the handoff screen can render "seeded N milestones and M epics" without re-querying the new workspace's beads database.
type RecycleWriter ¶
type RecycleWriter struct {
// contains filtered or unexported fields
}
RecycleWriter persists session.recycled events. Constructed via NewRecycleWriter; Run loops until ctx is cancelled.
func NewRecycleWriter ¶
func NewRecycleWriter(db *sql.DB) *RecycleWriter
NewRecycleWriter wraps the given *sql.DB. nil db is allowed — returns a writer whose Run is a pure no-op.
func StartRecycleWriter ¶
func StartRecycleWriter(ctx context.Context, op core.OrchestrationPlaneAdaptor, db *sql.DB) *RecycleWriter
StartRecycleWriter is the convenience constructor + launcher used by cmd/gemba serve. Subscribes the writer to the OrchestrationPlane (filtered to session.recycled events only) and runs the loop on a new goroutine. ctx cancellation terminates the goroutine.
Best-effort: a Subscribe failure is logged but doesn't propagate — the writer becomes a no-op and gemba serve continues. The retro pipeline simply won't have recycle audit data until the next restart.
func (*RecycleWriter) Insert ¶
func (w *RecycleWriter) Insert(ctx context.Context, ev core.OrchestrationEvent) error
Insert persists one session.recycled event. Exposed so tests can drive the writer without spinning up a Subscribe channel. Returns nil on success or a wrapped error on insert failure.
func (*RecycleWriter) Run ¶
func (w *RecycleWriter) Run(ctx context.Context, in <-chan core.OrchestrationEvent)
Run reads events from in until ctx is cancelled or in is closed. Each session.recycled event is persisted via Insert; non-fatal errors are logged and the loop continues.
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router is the package-level entry point. cmd/gemba passes in the embedded SPA filesystem so this package doesn't import the embed declaration directly. host carries the bound transport-plane adaptors so handlers can reach the WorkPlane / OrchestrationPlane without re-resolving them per request.
func NewRouter ¶
NewRouter builds the chi router. spa must be a filesystem rooted at the built Vite output (with an index.html at the top level); pass nil or an empty FS during development and the handler will return a helpful hint. host may be nil during early bring-up — handlers that need a WorkPlane must check Host() before dereferencing.
func (*Router) ActiveProject ¶
ActiveProject returns the project name the operator most recently switched to, or an empty string if no switch has occurred this session. Exposed for gm-root.17.7 (start-planning handoff) so it can read the just-created project without re-scanning the disk.
func (*Router) AttachBootstrap ¶
func (r *Router) AttachBootstrap(store BootstrapStore)
AttachBootstrap binds the BootstrapStore used by the /api/bootstrap/* handlers. Calling with nil removes the binding (handlers return 503). cmd/gemba serve calls this once at boot; tests inject memory stores per case.
func (*Router) AttachMetricsHTTPClient ¶
AttachMetricsHTTPClient overrides the http.Client the /api/v1/metrics/series proxy uses to call upstream Prometheus (gm-e9m0). Tests pass a client wired to httptest.Server so they don't depend on a real Prometheus instance. cmd/gemba serve does not call this — production runs use the default 15s-timeout client.
func (*Router) AttachMetricsHandler ¶
AttachMetricsHandler binds an http.Handler to GET /metrics. Calling with nil leaves the route returning 503 — useful for tests that want to skip the metrics surface entirely. cmd/gemba serve calls this with a metrics.Collector.Handler() once at boot.
func (*Router) AttachNewProject ¶
func (r *Router) AttachNewProject(store NewProjectStore, turner SkillTurner, ratifier NewProjectRatifier)
AttachNewProject binds the /onboard session store + skill turner + ratify executor (gm-root.17.6). Calling with a nil store removes the binding (handlers return 503). cmd/gemba serve calls this once at boot; tests inject fixtures per case.
turner may be nil — when so, NewStubSkillTurner takes over. ratifier may be nil too — when so, /ratify returns 503 (the conversation surface still works for SPA preview / screenshot purposes).
func (*Router) AttachPersonaDispatcher ¶
func (r *Router) AttachPersonaDispatcher(d *persona.Dispatcher, sr *corepersona.SkillRegistry, pr *corepersona.Registry)
AttachPersonaDispatcher binds the persona consult dispatcher, the skill registry it draws from, and the persona registry the POST endpoint resolves persona_id against (gm-twp2). cmd/gemba serve calls this once at boot after parsing personas + registering skills; tests attach a fixture-built dispatcher per case. Routes under /api/skills* and /api/consults* return 503 until at least the dispatcher + skill registry are bound; POST /api/consults additionally requires the persona registry (the read endpoints don't).
Any argument may be nil to detach (tests that re-use a Router across cases set then clear).
func (*Router) AttachPersonasDir ¶
AttachPersonasDir binds the directory the /api/personas handler scans. Empty / unset falls back to "<cwd>/.gemba/personas".
func (*Router) AttachPhase ¶
AttachPhase binds the phase.Store the /api/v1/phase handlers read from. cmd/gemba serve calls this once at boot after seeding the initial WorkspaceState; tests inject a phase.NewMemoryStore (or a pre-seeded NewSeededMemoryStore) per case. Calling with nil detaches the binding — handlers return 503 until the next AttachPhase call.
func (*Router) AttachPoolConfig ¶
AttachPoolConfig binds the editor's load/save target. path is the pool.toml filesystem path; maxParallel is the host-wide concurrent pane cap (the same value [pool] clamp arithmetic uses); reservedForManual is the [pool] reserved_for_manual default after the cascade. cmd/gemba serve calls this once at boot.
func (*Router) AttachPools ¶
func (r *Router) AttachPools(resolved []config.ResolvedPool)
AttachPools binds the resolved pool list to the router. cmd/gemba serve calls this once at startup after Resolve runs. Calling with a zero-length slice is the "no pools configured" path — the endpoint then returns an empty list.
func (*Router) AttachProjects ¶
func (r *Router) AttachProjects(cfg AttachConfig)
AttachProjects wires the projects/attach handler dependencies. Optional — zero-value Router uses production defaults (defaultBeadsPinger, execRunner, time.Now). Tests call this to inject stubs.
func (*Router) AttachWalk ¶
AttachWalk binds the walk.Store + walk.Sources used by the /api/v1/walks/* handlers. Calling with nil store removes the binding (handlers return 503). Sources is zero-value-safe — a nil EscalationLister / HITLLister / GateFailureLister / WorkItemLister contributes zero items to BuildAgenda. cmd/gemba serve calls this once at boot; tests inject fakes per case.
func (*Router) AttachWalkSummary ¶
func (r *Router) AttachWalkSummary(s *walk_summary.Skill)
AttachWalkSummary binds the Documentarian's walk_summary skill (gm-77u). When attached, the End handler invokes Run after the store transition so a markdown artifact lands under docs/walks/<date>-<slug>.md. Pass nil to disable — the End handler still records the transition and emits the SSE event, just without producing the artifact.
func (*Router) AttachWorkflowClient ¶
AttachWorkflowClient binds the workflow.Client the /api/workflows/* surface dispatches to (gm-e12.22.2). Until called, every workflow handler returns 503 adaptor_not_configured. cmd/gemba serve calls this once at boot with a client backed by the bd CLI on PATH; tests inject a stub Runner via NewClientWithRunner.
func (*Router) Close ¶
func (r *Router) Close()
Close stops the HealthBus ticker and tears down the events hub. Safe to call when StartHealthBus was never invoked.
func (*Router) EventsHub ¶
EventsHub returns the GembaEvent fan-out broker. cmd/gemba serve uses this to AttachOrchestrationStream against the registered adaptor's Subscribe() output, so adaptor events fan to /events SSE subscribers. Returns nil only when the router was constructed without one (zero-value/test paths).
func (*Router) HealthBus ¶
HealthBus returns the registry.HealthBus this router caches adaptor status against. cmd/gemba serve uses this to wire the gemba walk's BeadsDegradedLister against the same probe ticker the SPA banner reads from (gm-vch2). Returns nil only when the router was built without one — test paths that don't exercise adaptor health.
func (*Router) Host ¶
Host returns the api.Host this router was built with, or nil if the router was constructed without one. Tests and handlers use this to reach the registered WorkPlane / OrchestrationPlane.
func (*Router) InstanceID ¶
InstanceID returns the per-process boot id stamped on startup- immutable responses (capabilities, adaptors). The SPA stores the first id it sees and full-reloads if a later response shows a different id, which is how we detect a server restart with new config without a capabilities-changed channel. gm-6m60.
func (*Router) PhaseStore ¶
PhaseStore returns the phase.Store this router was last attached with, or nil. cmd/gemba serve uses this to share the store with other subsystems (e.g. gm-3on's Purview-as-gate path will read it to decide whether a Manager mutation is allowed).
func (*Router) ServeHTTP ¶
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)
ServeHTTP dispatches through the chi mux built in NewRouter. Router satisfies http.Handler so callers can hand it directly to http.Server.
func (*Router) StartHealthBus ¶
func (r *Router) StartHealthBus()
StartHealthBus preserves the old serve wiring hook. With the current zero interval it is intentionally a no-op: adaptor health is checked by explicit refresh paths, not by a periodic background probe. gm-root.7.
type SkillTurner ¶
type SkillTurner interface {
Turn(ctx context.Context, in NewProjectState, message string, edits map[string]interface{}) (out NewProjectState, reply string, err error)
// Greeting returns the assistant's opening line on /start. Empty
// is a valid greeting (the SPA renders nothing).
Greeting() string
// Probe is invoked on /start before allocating a session and
// reports whether the turner can serve the request. Real-mode
// implementations return an error wrapping the operator-facing
// diagnostic (e.g. "no LLM client configured") so the handler
// can return 503 with the diagnostic in the body. Stub turners
// return nil. May be called concurrently.
Probe(ctx context.Context) error
}
SkillTurner is the contract the Onboarder persona host (gm-root.17.10) implements: take a state + an operator message + any in-place edits and return the next state + a reply for the conversation pane.
Production serve wiring installs the real Onboarder persona host. A stub implementation remains available for tests and fallback setups; it mirrors the e2e fixture's deterministic plan-tree mutation so screenshot tests stay stable.
func NewStubSkillTurner ¶
func NewStubSkillTurner() SkillTurner
NewStubSkillTurner returns the placeholder turner. Production serve wiring uses the real Onboarder via AttachNewProject(... real-turner) (gm-root.17.10).
Source Files
¶
- adaptors.go
- adaptors_stream.go
- agent_groups.go
- agents.go
- autodispatch_wire.go
- beads_health.go
- beads_history.go
- bootstrap.go
- bootstrap_drafts.go
- capabilities.go
- consults.go
- consults_apply.go
- consults_post.go
- doc.go
- embedded_personas.go
- escalations.go
- escalations_test_inject.go
- events_stream.go
- fs.go
- interactions.go
- metrics_query.go
- milestone_autoclose.go
- milestone_naming.go
- newproject.go
- newproject_ratify.go
- nonce.go
- onboarding_setup.go
- openapi.go
- operational_context.go
- orchestration_mutations.go
- orchestration_state.go
- persona_files.go
- personas.go
- phase.go
- planner_coach.go
- pool_config.go
- pools.go
- projects.go
- projects_adoptable.go
- projects_attach.go
- projects_bind.go
- projects_clone.go
- recycles.go
- repositories.go
- router.go
- scope_status.go
- sessions.go
- skills.go
- spa.go
- spec_kit.go
- sprints.go
- walks.go
- walks_perspectives.go
- work_items.go
- work_items_cascade.go
- work_items_write.go
- workflows.go
- workitem_notify.go
- workspaces.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package dispatch holds the server-side routing policy that decides whether a new bead should co-locate in an existing intra-parallel session or trigger a fresh pane spawn (gm-root.16.4).
|
Package dispatch holds the server-side routing policy that decides whether a new bead should co-locate in an existing intra-parallel session or trigger a fresh pane spawn (gm-root.16.4). |
|
Package httperr is the single place every gemba data handler goes to turn a Go error into the wire-stable JSON error envelope.
|
Package httperr is the single place every gemba data handler goes to turn a Go error into the wire-stable JSON error envelope. |
|
Package metrics binds the GembaEvent stream to a Prometheus /metrics endpoint so operators can scrape adaptor health, session activity, escalation pressure, and budget posture without standing up an OTEL collector.
|
Package metrics binds the GembaEvent stream to a Prometheus /metrics endpoint so operators can scrape adaptor health, session activity, escalation pressure, and budget posture without standing up an OTEL collector. |