Agent-served solution data
Status: Accepted · Date: 2026-07-02 · Area: Data
Context
Originally both the agent (writer) and the orbit-webapp reader mounted the solution repository and read it from disk — the reader on a read-only mount. That coupled the reader’s correctness to filesystem semantics that are unreliable in both target environments: SMB-backed Azure Files shares and macOS bind mounts drop inotify events, so a filesystem watcher cannot be trusted to tell the reader when the tree changed. It also meant two processes interpreting the same working tree while the agent switches git branches under it.
Decision
- The agent is the sole owner of the
/repomount. It loads the wholeRawSolutionTreeinto memory (SolutionModel) and serves it over HTTP; the reader webapp has no repo mount at all. - The contract:
GET /solution/versionreturns a per-process monotonicversionplus a per-bootinstanceUUID — cache identity is always the (instance, version) pair, never version alone, because the counter restarts with the agent.GET /solution/treeserves the tree (gzip, ETag"<instance>:<version>"with 304 support);POST /solution/reloadis the manual escape hatch;GET /sources/*serves the code explorer’s data; and an SSE stream emitssolution.updatedevents. - There is deliberately no filesystem watcher. Reloads are triggered at the writer’s well-defined settled moments — startup, per-task commit (so the reader shows live mid-request progress), request merge, request failure, post-crash recovery, manual — serialized and coalescing.
- The webapp fetches through a server-only client (
agent-source.ts; the browser never talks to the agent), rebuilds its rendered model only when (instance, version) moved, and serves stale from cache with a warning if the agent is down. Live refresh reaches the browser via an/api/orbit-eventsSSE proxy and an in-place RSC refresh. - The loader itself moved to the shared package
implementation/shared/trajectory-loader(@maxq/trajectory-loader, afile:dependency of both codebases).
Consequences
- One process interprets the repository; the reader can never observe a half-switched branch, and the agent’s single replica (ADR-004) is what makes the one in-memory model plus event bus safe. The webapp scales 0–3 replicas with per-replica caches, correct by cache keying.
- On Azure the reader app needs no share mount and no read-only storage link anymore — provisioning gets simpler.
- Accepted trade-off: out-of-band edits to the solution repo no longer
appear in the reader until a reload is triggered (in local dev:
curl -X POST http://localhost:3010/solution/reload). Task runs reload automatically. - The shared-loader
file:dependency widens both Docker build contexts toimplementation/rather than the individual codebase folders.
Evidence
implementation/maxq-orbit-agent/codebase/src/solution/SolutionModel.ts— the in-memory model and reload semanticsimplementation/orbit-webapp/codebase/src/lib/solution/agent-source.tsandsrc/lib/solution/index.ts— the HTTP client and (instance, version) cacheimplementation/shared/trajectory-loader/— the extracted loaderdesigns/agent-served-solution-data.md(§11 decisions, §12 implementation record),memory/agent-served-solution-data.md