server

package
v0.0.0-...-86b21bc Latest Latest
Warning

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

Go to latest
Published: May 8, 2026 License: MIT Imports: 56 Imported by: 0

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

View Source
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

View Source
var ErrBootstrapNotFound = errors.New("bootstrap: analysis not found")

ErrBootstrapNotFound is returned by BootstrapStore.Get / Commit when the analysis id is unknown.

View Source
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

type AdoptableLister func(ctx context.Context, baseURL string) ([]adoptableDB, error)

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

type BeadsPinger func(ctx context.Context, beadsURL string) error

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

type CommandRunner func(ctx context.Context, dir, name string, args ...string) ([]byte, error)

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 DraftEpic

type DraftEpic = newproject.DraftEpic

DraftEpic is one epic in the plan tree.

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

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

func NewRouter(cfg config.ServeConfig, spa fs.FS, host *api.Host) *Router

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

func (r *Router) ActiveProject() string

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

func (r *Router) AttachMetricsHTTPClient(c *http.Client)

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

func (r *Router) AttachMetricsHandler(h http.Handler)

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

func (r *Router) AttachPersonasDir(dir string)

AttachPersonasDir binds the directory the /api/personas handler scans. Empty / unset falls back to "<cwd>/.gemba/personas".

func (*Router) AttachPhase

func (r *Router) AttachPhase(store phase.Store)

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

func (r *Router) AttachPoolConfig(path string, maxParallel, reservedForManual int)

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

func (r *Router) AttachWalk(store walk.Store, sources walk.Sources)

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

func (r *Router) AttachWorkflowClient(c *workflow.Client)

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

func (r *Router) EventsHub() *events.Hub

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

func (r *Router) HealthBus() *registry.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

func (r *Router) Host() *api.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

func (r *Router) InstanceID() string

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

func (r *Router) PhaseStore() phase.Store

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).

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.

Jump to

Keyboard shortcuts

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