The engine — modules and packages

Three Gradle modules, 108 Kotlin files in the main source sets. This page is the map: what each module owns, and every package inside it with the file that holds it.

ModuleFilesOwnsDepends on
:core21the wire contract (v7), the canonical codec, the client reducer + fold + transport, the scripted fake, the Loom side, the offline entitlement verifierKotlin stdlib
:daemon82the store, the bus, the turn loop, the tool catalog, delegation, the rails, the model adapters, the surface:core, SQLite JDBC
:cli5the reference client: a terminal that steps the same Reducer:core

:core — the contract and the client machine

dev.staage — 1 file

FileWhat it is
Brand.ktthe app's identity in two registers: NAME = "Ståge" (display), ID = "staage" (identifier)

dev.staage.wire — 4 files

FileWhat it is
Wire.ktthe phone ↔ host protocol as a typed artifact; VERSION = 7
WireCodec.ktone Wire message ↔ one canonical JSON line
Json.kta canonical JSON model, hand-built so the codec has no dependency and no ambiguity
Roster.ktthe v3 frames — roster, thread creation, search, the model list

dev.staage.client — 4 files

FileWhat it is
State.ktclient state — the shape the reducer owns
Reducer.ktthe client state machine
Fold.ktthe settled fold — how the roster is ordered (attention first, then activity)
Journal.ktdeterministic rendering of a reducer run (the receipts' substrate)

dev.staage.net — 2 files

FileWhat it is
MacClient.ktthe app's transport to the host
NdjsonStream.ktthe read side: NDJSON lines in, Wire messages out

dev.staage.loom — 4 files

The host half of rehydrate.

FileWhat it is
LaneJournal.kta bounded per-lane journal with retention and eviction
Pages.kthistory pages — one page's rules and nothing else
LaneFeed.ktthe feed seam — where lane events come from
LoomSurface.ktthe protocol adapter that fronts the engine

dev.staage.license — 4 files

The offline entitlement verifier: parse, verify an Ed25519 signature, answer two facts separately.

FileWhat it is
EntitlementFormat.ktthe file format — parse only; no keys, no clock, no I/O
Entitlement.ktthe verifier
Licence.ktthe parsed payload's ten fields, as values
Verdict.ktthe two facts, returned separately

dev.staage.fake — 2 files

FileWhat it is
ScriptedServer.kta scripted server, in memory
ScriptedStream.ktone step of a scripted stream

:daemon — the runtime

82 files: 70 in dev.staage.daemon, 6 in provider, 5 in rail, 1 in fake. Grouped by what they do, not by alphabet — this is the map an operator or a contributor actually needs.

Boot, loop and the turn

FileWhat it is
Daemon.ktthe daemon — store, bus, loop, surface, composed
Loop.ktthe loop, behind interfaces (Provider, Tool, Approver)
TurnExecutor.ktone turn, executed: rounds, tool calls, approvals, usage
TurnMachine.ktthe turn executor's state machine, named
TurnRound.ktone round with the provider
TurnTools.ktone assistant message's tool calls, sequentially, in order
TurnToolCalls.ktthe opening every tool call shares — its persisted tool_use block
TurnToolChoice.ktthe tool-choice reading and the tool_use/tool_result conversation shape
TurnBlocks.ktblock flushing
TurnCompaction.ktthe request's lens — compaction as a projection of the record
TurnSteer.ktsteering at the safe checkpoint — the executor's half
TurnUsage.kt / TurnUsageAccumulator.ktthe turn's usage, and the sum across rounds
Guard.ktthe loop's airbags and its result boundary
Continuation.ktcontinuation — the task walks
ApprovalGate.ktthe approval round-trip, made mechanical

Store, journal, bus

FileWhat it is
Db.ktthe one connection to our store, and the one writer
Schema.ktour schema, our names, migrations from the first commit
Store.ktthe write paths, the read paths, and the rules
StoreSql.kt / StoreRows.kt / Model.ktSQL plumbing, row mappers, the rows as values
StoreRoles.ktthe three roles a store can play, and the one guard between them
JournalStore.ktappends one lane event; the ordinal is the lane's durable order
MessageStore.ktappend one steering sentence to a running turn
ToolCallStore.ktthe tool-call rows
BlobStore.ktput one picture into the store, answer the name it earned
Search.ktserver-side search over our store
LaneBus.ktour bus — the in-process fan-out
EventRecorder.ktpersistence and fan-out, in that order
Spool.ktthe runtime spool files — a stable floor, never the system temp dir
Clock.kttime, injected
Digest.kta deterministic digest, in one place
Activity.ktthe daemon-global monotonic counter the roster reads

Tools and the catalog

FileWhat it is
Toolset.ktthe tool registry — the single declaration of what the model may call
Tools.ktthe v1 toolset (scratch_read)
FileTools.kt / FileState.kt / FileModes.ktfile_read, file_edit, file_write — the sealed outcome vocabulary, the modes
FindTools.kt / FileLocate.ktfile_find, file_search — the locating mechanics both share
SkillTools.ktthe skills mechanism — know-how cards listed and loaded on demand
Bash.ktrun one shell command — the resident's own command line
BashSandbox.ktthe box a bash command runs in — a value, not a second code path
DevCheck.ktrun one gate and read the result
Plan.ktthe plan — the turn's spine, declared by the agent and read by the human
Workdesk.ktthe desk — the thread's workdesk page
EffectClass.ktwhat a tool does to the world — the class the approval decision reads
ToolProfile.ktthe turn's tool profile: which declared tools this turn may use
EnvScrub.ktcredential-shaped variables, removed by shape rather than by name
TokenBoundary.ktthe bearer is not workspace-readable
Workspace.ktthe workspace root — one directory the file instruments may touch
Compact.ktcompaction — the request is a projection of the record
Senses.ktthe Senses — a local, opt-in record of what this daemon actually did
StandIn.ktthe stand-in — what answers when no client does
Steer.ktsteering — the human's hand lands at a checkpoint, not between two wires

Delegation, workforce, verification, write children

FileWhat it is
Delegation.ktdelegation — a child is a contract, not a transcript
Deck.ktthe deck — the waiting room between a child that cannot move without its parent
Workers.kt / WorkerModels.kt / WorkerRules.ktthe workforce as one pure state machine: statuses, budgets, typed outcomes, and one boolean per rule
Verification.kt / Verifier.ktverification — a verifier reads the artifact, not the builder; one verifier child per task.verify
WriteChild.kt / WriteSession.kt / WriteTools.ktthe write child: a disposable worktree, a diff with a digest, and apply_change as the one writer
Worktrees.ktthe process seam — one child, one disposable checkout

The surface and the providers

FileWhat it is
DaemonSurface.ktthe wire, served by our daemon
Models.ktwhat this daemon can run, and whether it may
provider/Sse.ktthe transport seam, and the one stream engine both adapters sit on
provider/Anthropic.ktAnthropic's SSE event flow
provider/OpenAiCompat.ktthe OpenAI-compatible Chat Completions API — the generalization test
provider/Wire.ktthe provider-agnostic vocabulary the adapters share
provider/Targets.ktthe builder's provider, chosen from the environment's credentials
provider/Replay.ktthe recorded-stream transport (the gates' world, no network)
fake/Scripted.ktthe scripted brain

The rails — dev.staage.daemon.rail

FileWhat it is
Rail.ktthe rail seam — one request/response read, as a value
Rails.ktthe rails as shipped: what the model is shown, and what the environment enables
Bank.ktthe knowledge rail — read-only search and fetch over the user's own posts (bank_search, bank_get)
Librarian.ktthe librarian — one approved read that hunts the store from several angles (librarian)
Research.ktthe research rail — one live question answered from the open web (research)

A rail is offered only when it can answer. Every rail needs a credential; a rail whose credential is absent is not declared at all, because a tool the model can see and cannot use is a trap — the model calls it, the human is asked to approve a call that cannot succeed, and the turn burns a round trip to learn something boot already knew. The daemon prints which rails are offered and, for each that is not, the variable that would enable it; --require-rails turns that report into a refusal.


:cli — the reference client

5 files.

FileWhat it is
Command.ktthe command language — pure syntax
Session.ktthe session: the client, the reducer, the honest refusals
Repl.ktthe command interpreter
Renderer.ktrendering — the honest face of the machine
Main.ktoptions and the one entry point

The CLI is not a demo. It is the CLI-testable maxima: every UI claim the phone makes is provable off-screen first, because the terminal steps the same Reducer the phone steps.

Commands: lanes, open <lane>, state, create, rename, desk/undesk, close, archive, workdesk show|write, search, prompt, steer, abort, approve, deny, answer, wait, deck, models, detach, task list|cancel|message|evidence|verify|changes|apply, help, quit.


How the boot assembles the catalog

script/daemon-run/StaageDaemon.kt is the only place the toolset is composed, and it is composed from declarations — never from a mutable map:

val toolset = Toolset.of(
    listOf(V1Tools.scratchRead(scratch)) +
        FileTools.declarations(workspace) +
        FindTools.declarations(workspace) +
        SkillTools.declarations(skills) +
        Bash.declaration(workspace, bashSandbox) +
        listOf(Plan.declaration()) +
        listOf(Workdesk.declaration(store, clock)) +
        (delegateHolder?.let { listOf(Delegation.declaration()) } ?: emptyList()) +
        (writeApplyHolder?.let { listOf(WriteApply.declaration(it)) } ?: emptyList()) +
        rails.declarations(),
)

Toolset keeps catalog (what the provider renders) and tools (what the loop dispatches to) as two projections of one list — they cannot drift. Names are host-assigned and collision-free, and each descriptor is pinned by digest of (origin, raw name, schema), so a material change is a different tool and an approval can be bound to it. See The tool catalog.