Internal Packages
One section per internal/ package. Import paths are
github.com/koryph/koryph/internal/<name>.
account
Constructs the child environment for a dispatch and verifies the logged-in
Claude identity, fail-closed. Supports subscription (OAuth) and api-key
billing modes.
Profile— resolved account context (config dir, expected email)Identity— email address read from.claude.jsonBillingMode—"subscription"|"api-key"Env(p, billing, apiKey)— full[]stringchild env; scrubs/re-injects credentialsVerify(ctx, p)/VerifyExpected(ctx, p, email)— read and compare identity
anthro
Thin wrapper around anthropic-sdk-go for the two operations koryph uses
directly: single-message inference and pre-flight cost estimation.
Client— holds SDK client;NewClient(keyEnvVar)resolves the key from envMsgReq— request envelope (model, messages, max tokens)Usage— input/output token counts;BatchResult— aggregate batch outcomeEstimateUSD(reqs)— cost estimate for a slice ofMsgReq
beads
Adapts the bd CLI. The only package that shells out to bd; all other
packages consume its types.
Adapter— wraps a repo root;New(repoRoot)is the constructorIssue— parsed beads issue (ID, title, status, labels, …)ReadyOpts— filters forbd readyqueries
commands
Ships the koryph-* Claude slash commands (//go:embed koryph-*.md) and
installs them into a project's .claude/commands/. Installed at onboarding so
koryph semantics hold whether koryph is run explicitly or implied by a
prompt.
FS— embedded slash-command templatesInstall(root, force)— copy commands viascaffold.CopyEmbed
dispatch
Launches one bead as a detached, headless claude process inside its
worktree and tracks the resulting PID/stream.
Spec— everything needed to launch one agent (bead, env, paths)Handle— live process reference (PID, stream path, start time)Backend/CLIBackend— interface + production implementation over theclaudebinaryParseResultCost(streamPath)— extract USD spend from transcriptAlive(pid)/StopGraceful(pid)— process lifecycle helpers
engine
The wave loop: scan → batch → preflight → dispatch → poll → stages → review →
merge. Main orchestration entry point called by cmd/.
Options— full run config (project ID, wave width, dispatch mode, auto-merge, review flags, …)Outcome— summary counts (dispatched, merged, failed, blocked)Run(ctx, opts)— blocking entry point; returns when the run terminates
Internal files: poll.go (heartbeat/completion, rate-limit requeue, AIMD
signal forwarding), recover.go (resume logic), wave.go (engine-side wave
assembly, rolling-mode refill loop, in-flight footprint map), pipeline.go
(post-implement stage execution), govern.go (AIMD report-rate-limit helper).
Requeues refresh the worktree onto current main first (rebuild when no commits,
rebase when there are) so a retry never runs a stale checkout.
DispatchMode (from Options or project.Config.DispatchMode) selects
wave (wait-for-batch, default) or rolling (continuous-refill). Both modes
share poll primitives; rolling additionally passes active in-flight footprints
(sched.Opts.Active) to every BuildWave call so freshly-picked candidates
cannot clash with running beads. Poll interval is poll_seconds from project
config (default 10), overridden by KORYPH_POLL_SEC or Options.PollSec.
execx
Runs external commands with an explicit working directory and environment. All engine subprocess calls go through here.
Cmd— command spec (bin, args, dir, env, timeout);Result— exit code + outputRun(ctx, c)/MustSucceed(ctx, c)— execute; latter errors on non-zero exitBaseEnv(remove...)— current environment with named vars strippedLookPath(name)— reports whethernameis on$PATH
fsx
Small filesystem helpers shared across the engine. All writes are atomic (write-to-temp + rename) to prevent torn files on crash.
WriteAtomic/WriteJSONAtomic— atomic byte-slice and JSON writesReadJSON(path, v)— unmarshal file intovAppendLine(path, line)— append one newline-terminated record;Exists(path)— stat check
govern
Machine-global concurrency governor: a cross-process cap on concurrently running
agents (across all projects) so independent koryph run invocations cannot
collectively breach the Claude API rate limits. Coordinates via lease + demand
files under ~/.koryph/slots guarded by a flock — no daemon. See
global-governor.md.
Store—Acquire(cap-checked reserve) /Release/Prune/RefreshDemand/DropDemand/Snapshot/Cap/SetCap/SetAdaptiveCap/ReportRateLimit/EffectiveCap/AIMDStatusConfig— serialisedgovernor.json; includes AIMD overlay fields (Adaptive,HardMax,DynamicCap) and settle/breaker/smoothing fields (SettleSeconds,SettleUntil,BreakSeconds,BreakerState,MinDispatchIntervalSeconds, …)Lease/Demand— on-disk records- Fair-share:
floor(cap/n)per demander, rotating remainder (no starvation)
AIMD adaptive overlay (--adaptive). When enabled, EffectiveCap floats
between 1 and HardMax instead of pinning to MaxGlobalAgents: +1 every
5 minutes of quiet (additive probe) / halve on a rate-limit signal (multiplicative
decrease). Hardened by three koryph-2im.11 mechanisms — all Adaptive-gated:
- Settle window (
SettleSeconds, default 120 s): freezes further cap changes in either direction after anyDynamicCapchange; the probe clock anchors on settle expiry. - Burst-scaled decrease: ≥3 distinct
(project, bead)rate-limit events within 30 s → divide by 4 instead of 2. - Circuit breaker (
BreakerState): opens when the cap is already at the floor or on 3 decreases within 10 minutes; denies all new admission machine-wide forBreakSeconds(default 300 s, doubling per consecutive re-open up to 3600 s); transitions half-open → admits exactly one probe → closes on clean release / re-opens on probe rate-limit. - Dispatch smoothing (
MinDispatchIntervalSeconds, default 3 s): machine-wide minimum inter-dispatch spacing, jittered ±50%, to prevent thundering-herd refills.
ledger
Owns the per-run ledger (JSONL on disk) and per-slot checkpoints. Classifies live runs to recommend the next orchestrator action.
Store— ledger DB;NewStore(repoRoot)with per-slotLockRun/Slot— top-level run and per-agent recordsManifest— immutable dispatch record;PlanState— wave snapshotDecision— recommended action (merge, retry, abandon, …);Probe— current observationsTerminal(status)— true if status is a final stateClassify(run, probe)— returns[]Decisionfor the orchestrator
merge
Lands a finished agent branch on the default branch after running the configured green gate.
Opts/Result— merge configuration and outcomeSlotLocker— interface abstracting per-slot locking (for tests)Protected(diffPaths, extra)— returns protected paths hit by a diffMerge(ctx, o)— main entry: run gate → ff-merge (squash optional)RunGate(ctx, dir, cmds)— execute green-gate commands; returnsok, output
metrics
Rolls up burn-rate and reliability baselines from the run ledger for reporting and quota decisions.
ModelStat/ProjectStat/Report— nested stats typesCollect(store, projectID)— read ledger, compute*ReportRender(r, w)— pretty-print report to anio.Writer
modelroute
Resolves a (stage, bead-labels, run-defaults, project-config) tuple to a
(model, effort) pair, with persona-file overrides and recovery upgrades.
Precedence (koryph-v8u.10): bead model:<tier> label > persona tier (via
the active runtime's model map, project-overridable) > persona model
(legacy pin) > hardcoded stage default — see agents/README.md's "Resolution
precedence" section.
Req/Resolution— request and resolved(model, effort);Req.RepoRoot/Req.ModelMapopt a caller into the persona-tier stepResolve(r)— main entry; consults label rules, then persona tier/ model, then defaultsPersonaFor(stage, stages)— picks persona name from stage mapRecoveryUpgrade(current)— escalates model for a recovery re-dispatchPersonaMeta(repoRoot, persona)— reads persona file →(model, effort, tier)
onboard
Inspects a repository, registers it in the registry, and validates that all prerequisites (hooks, beads, account identity) are satisfied.
Inventory— survey result (remotes, hooks, beads status, worktrees)RegisterOpts— identity, billing, and flag overrides forRegisterValidation/Check— validate output (slice of named pass/fail checks)Inspect(ctx, root)— non-mutating surveyRegister(ctx, store, inv, opts)— writeregistry.Recordfrom inventoryValidate(ctx, store, projectID, w)— check all prerequisites, print results
paths
Resolves all koryph machine-local state locations from $KORYPH_HOME
(default: ~/.koryph). No exported types; all functions return string.
KoryphHome · RegistryDir · QuotaDir · AuditLog · RunsIndex ·
PlanLogs(repoRoot) · KoryphRoot(repoRoot)
project
Loads the per-project adapter configuration (koryph.project.json).
Config— wave width, green-gate commands, footprint rules, dispatch mode, poll interval (PollSeconds), and AIMD-adjacent knobsFootprintRule— path-pattern → conflict-scope mappingDefault(projectID)/Load(repoRoot)— sensible defaults or parse from disk
Key scheduler fields: DispatchMode ("wave" | "rolling", default "wave"),
PollSeconds (poll tick override; 0 → engine default 10 s), MaxConcurrentSlots
(wave-width cap per project), DispatchStaggerSeconds (inter-agent launch spacing).
promptc
Compiles the dispatch prompt in a cache-stable, deterministic order so prompt-cache hits are maximised across re-dispatches.
Input— all variable sections (task, plan, project context, …)Compile(in)— assemble final prompt stringPreamble(engineVersion)— static koryph-protocol preamble
quota
Per-account usage governor. Estimates wave cost, tracks rolling-window spend, and gates or scales dispatch.
Config— daily/monthly caps and thresholds per accountUsage/Window/Level— spend snapshot, rolling measurement,"ok"|"warn"|"drain"|"stop"State(u, cfg)— deriveLevel;ScaleSlots(u, max)— reduce wave width under pressurePreflight(u, estimateUSD, cfg)— gate a wave before dispatchEstimateItem/EstimateWave— pre-flight USD estimates (claude's per-tier base prices)EstimateItemForRuntime/EstimateWaveForRuntime/DefaultConfigForRuntime— the same estimates, namespaced by runtime name (koryph-v8u.12): each runtime gets its own default per-tier USD base table (onlyclaude's carries real numbers today), selected by the runtime a bead actually resolves under (modelroute.ResolveRuntimeName); an unrecognized runtime name degrades to claude's table rather than erroring (an estimate is advisory governor input, never a fail-closed dispatch gate). Calibration keys ("<tier>:<size>") are deliberately not runtime-namespaced — only claude dispatches have ever recorded calibration, so existing~/.koryph/quota/*.jsonfiles keep estimating exactly as before.Record/LoadConfig/SaveConfig— persist actual spend and config
registry
Central multi-project registry stored under paths.RegistryDir().
One JSON file per project.
Store— registry root;NewStore()/NewStoreAt(home)Record— full project registration (ID, root, account, billing, hooks, …)Event— audit-log entry written on every mutation
Key Store methods: Get, Put, Delete, List, All.
review
Runs a read-only post-implementation review pass before a branch is merged.
Opts— branch, project root, model to useFinding— one review comment (file, line, severity, message)Verdict— pass/fail +[]FindingReview(ctx, o)— launch reviewer agent, collectVerdict
sched
Builds conflict-free waves from the beads ready frontier, respecting footprint rules and the wave-width cap.
Item/Wave— selected issues + deferred/blocked explanationsFootprint— RW conflict surface (Reads []string,Writes []string);Reason— why deferred/blockedFootprintFor(issue, cfg)— deriveFootprintfrom labels + project rulesConflicts(a, b)— true iff footprints share a token and at least one side holds it as a write (RWMutex semantics: two readers co-run)Eligible(issue, activeIDs)— can this issue be dispatched now?BuildWave(...)— main entry: frontier →Wave; acceptsOpts.Active(in-flight footprints keyed by bead id) for rolling-mode in-flight gating
Dispatch shape. Eligible skips any bead whose issue_type is
epic/feature/decision/merge-request, or that carries a no-dispatch,
refactor-core, or gt:* label — so a bead filed with the wrong type sits in
bd ready forever. FootprintFor derives conflict tokens with the precedence:
fp:read:<token>labels → read tokens (two readers of the same token co-run);fp:<token>labels (any other suffix) → write tokens (existing grammar, unchanged);area:*labels resolved through the project'sarea_map→ write tokens;- else
domain:unknown(a write token) — conflicts with every other unlabeled bead, serializing them 1-per-wave.
BuildWave also accepts Opts.Active (in-flight footprint map for rolling mode): a candidate
whose footprint Conflicts with any entry here is deferred before intra-batch greedy coloring
even runs, so a rolling refill can never clash with already-running beads. Label implementable
beads (task/bug/chore) with one area:* per area they touch: over-broad labeling only costs
parallelism, under-broad labeling risks a false-parallel merge conflict.
rules
Installs the koryph enforcement rules into a project: the hook scripts
(hooks/*.sh) and their wiring in .claude/settings.json. Hook scripts install
like agents (whole-file, hash-idempotent). settings.json is merged
additively — koryph's hooks and permission allow/deny entries are added,
every other key is preserved, and only an unparseable file blocks the merge.
Install(root, force)— install hooks + merge settingsMergeSettings(root, force)— additive settings merge →created/merged/unchanged/skipped
scaffold
Shared, hash-aware installer for binary-embedded assets (personas, commands,
hooks) into a project. Identical content is a no-op (unchanged); differing
content is skipped (warned) unless force, then overwritten.
Result/ action constants (installed/overwritten/unchanged/skipped)CopyEmbed(fsys, destDir, force, perm)— copy every embedded file with permConflicts(results)/Count(results, action)— reporting helpers
stage
Runs one post-implement pipeline stage: a write-capable persona agent executed synchronously in the implementer's worktree (before review/merge) under the same account/billing/identity guarantees as a dispatch.
Opts— worktree, branch, resolved persona + model, per-stage prompt, profile/billingResult—Ran/OK/CostUSD/NoteRun(ctx, o)— verify identity, run thedontAskclaude one-shot, persist the envelope, report cost
version
Holds the engine's semantic version (set at build time via -ldflags) and
version-requirement checking. Single entry point: Satisfied(have, want)
reports whether have meets the want semver constraint.
worktree
Creates and manages per-bead git worktrees (one branch per active issue).
Info— metadata (path, branch, bead ID, dirty flag)EnsureOpts/RefreshOpts/RefreshResult— lifecycle options and resultsBranchFor(beadID)— returns"agent/<beadID>"List(ctx, repoRoot)— all worktrees for the repoEnsure(ctx, o)— create-or-reuse worktree for a beadBootstrap(ctx, path, cmds, env)— run bootstrap commands inside worktreeRefresh(ctx, o)— rebase or snapshot an existing worktreeRemove(ctx, path, force)— delete worktree and prune branchPatchSnapshot/DeleteBranch— export diff patch, remove remote-tracked branch