Update and consolidate boot sequence documentation

This commit is contained in:
Ollie Agent 2026-08-16 19:23:27 +02:00
parent 30fdc7d673
commit 97b14dfce3
2 changed files with 54 additions and 54 deletions

View File

@ -64,3 +64,57 @@ Installed runtime data includes backend configuration, agent definitions, prompt
## Design constraints
The core deliberately avoids framework-heavy abstractions. Provider selection, tool discovery, sandboxing, and user interfaces are replaceable boundaries; the stable contract is the 9P namespace and its blocking read/write behavior.
## Boot sequence
`olliesrv` starts in two layers: `main()` resolves the 9P namespace and delegates to `runServer`, then the server builds the filesystem, starts listeners, restores sessions, and waits for termination.
### Call chain
```text
main()
│
├── flag.CommandLine.Parse(os.Args[1:])
├── client.Namespace() — resolve the 9P namespace
├── filepath.Join(ns, "ollie") — derive the Unix socket path
└── runServer(sockPath)
│
├── util.EnsureEnv() — create/populate XDG runtime paths
├── remove stale ollie and toolsrv sockets
├── olog.NewSink(...) — configure logging from $OLLIE_LOG
├── agent.AgentsDirs() — resolve agent configuration
├── context.WithCancel(...) — create daemon lifetime context
├── fs.NewModelCache() — create the asynchronous model cache
├── dbus.SessionBus() — optionally configure desktop bypass notifications
├── fs.NewRoot(fs.Config{...})
│ ├── session.Init(...) — install runtime dependencies
│ ├── buildTreeSpec(...) — create the namespace specification
│ └── virtfs.BuildTree — build the 9P filesystem tree
├── New(Config{RootTree: rootTree}) — create the 9P server
├── go modelCache.Get() — prefetch models
├── net.Listen("unix", sockPath)
├── accept loop — each connection runs srv.Start(conn)
├── fs.RestoreSessions() — restore persisted sessions after listening
├── optional net.Listen("tcp", tcpAddr)
└── wait for SIGINT/SIGTERM
```
### Why `fs.NewRoot` is the composition root
The 9P filesystem is the runtime boundary. `fs.NewRoot` installs the session package configuration and builds the complete namespace from the filesystem specification. It does not restore sessions; restoration is deliberately deferred until the Unix listener is ready so restored agents can be wired to the live tree and event paths.
`main.go` is deliberately thin: parse flags, resolve the namespace, and call `runServer`. `runServer` owns startup, listener management, signal handling, and shutdown.
### Key ordering constraints
1. `util.EnsureEnv()` runs before paths, logging, or persistent state are used.
2. The stale Unix socket is removed before `net.Listen("unix", sockPath)`.
3. `session.Init()` runs inside `fs.NewRoot` before any session restoration.
4. The model cache is created before the root and prefetched asynchronously after the server is built.
5. `fs.RestoreSessions()` runs after the Unix listener and accept loop are ready.
6. The optional TCP listener starts after session restoration and uses the same 9P server.
7. Shutdown closes listeners, cancels the daemon context, kills the server, removes the Unix socket, and flushes logs.
### Session restoration
`fs.RestoreSessions()` reads JSON files from the active-session directory through `session.RestoreAllSessions()`. Restoration runs concurrently per file. Invalid files are logged and removed. Paused sessions rebuild minimal agents without a tool server or backend connection; active sessions rebuild their agents and connections. Restored agents are replayed into the live filesystem and wired to agent events after the listener is ready.

View File

@ -1,54 +0,0 @@
# Boot Sequence
How `olliesrv` starts up, from `main()` to serving 9P requests.
## Call chain
```
main()
│
├── env.EnsureDefaults() — populate $XDG_CONFIG_HOME/ollie/ if missing
├── olog.NewSink() — stderr/stdout log sink
├── fs.NewModelCache() — async LLM model list (prefetched later)
│
├── fs.NewRoot(cfg) ← composition root
│ │
│ ├── session.Init(...) — set package-level config (ctx, dirs, logger)
│ ├── build RootState — allocate SessionNodes map
│ ├── fsedsl.BuildTree(spec) — walk fs/spec.go, produce *Tree with lazy resolution
│ └── session.RestoreAllSessions()
│ └── for each .json in sessions/active/
│ ├── unmarshal PersistedSession
│ ├── rebuild Session + Agents (paused)
│ └── register in session registry
│
├── bypass.NewBroker(...) — start bypass socket (sandbox escape approval)
├── New(serverCfg) — create 9P server wrapping the tree
├── net.Listen("unix", sockPath) — bind the 9P socket
└── accept loop — each conn gets srv.Start(conn)
```
## Why `fs.NewRoot` is the composition root
The 9P filesystem IS the runtime. Every session, agent, and tool server is
reachable only through the namespace. So the filesystem package owns
initialization: it creates the tree, initializes the session registry, and
restores persisted state — all before a single 9P connection is accepted.
`main.go` is deliberately thin: parse flags, set up logging, call `fs.NewRoot`,
start the listener. All domain logic lives in the packages.
## Key ordering constraints
1. `session.Init()` must be called before `RestoreAllSessions()` (sets pkg config).
2. `RootState` must be allocated before `BuildTree()` (closures capture the pointer).
3. `BuildTree()` must complete before `RestoreAllSessions()` (restored sessions
create nodes in the tree).
4. The bypass broker starts AFTER the tree is built but BEFORE the accept loop
(tool execution needs the broker socket).
## Session restoration
Restored sessions come back **paused**. They have agents with history but no
running tool server or LLM connection. The first `Resume` (via ctl write)
re-dials the tool server and reconnects the backend.