Files
sausage/DECISIONS.md
T
surma 93f092e02b cargo.build: a persistent build cache shared by all worlds
- The cargo target folder is <cache>/cargo-target, next to Wasmtime's
  cache, which moves to <cache>/wasmtime: Wasmtime's cleanup worker
  deletes unknown entries in its folder, also Cargo's artifacts.
- Builds use the SDK at <cache>/sdk/<content hash>/, so cargo reuses
  the compiled SDK in every world. /plugins/cargo/sdk/ stays for agents.
- Tests run in the release profile and share compiled crates with the
  release build.
- The cargo and cli tests use a persistent cache in CARGO_TARGET_TMPDIR:
  warm, the cargo suite takes 11 s (was 235 s) and the cli suite 6 s
  (was 149 s).
- nix flake check runs ignored stress tests. AGENTS.md: rules for fast
  tests.
2026-10-23 08:05:15 +01:00

87 lines
4.5 KiB
Markdown

# Decisions
Why Sausage is the way it is. `PROTOCOL.md` and the code own the details.
When this file disagrees with them, they win.
## The shape
- **JSON-RPC, not component interfaces.** Every language can read and write
lines of JSON, and models know JSON-RPC from LSP and MCP. A call has a
result, like an LLM tool call.
- **A process per call.** WASI has no threads, so processes give
parallelism. A new version applies at the next call, and old processes
retire after a grace period.
- **Attached by default.** A process does not outlive its caller by
accident. A long-running process must ask for `detach`.
- **Services in one file.** Only the core starts and restarts them, so
upgrades and singletons need no logic in plugins.
- **Files to inspect, calls to act.** This keeps the core API small, and the
agent's file tools are enough to explore the world.
- **The core accepts only `.wasm` files.** It knows no source language.
`cargo` is one way to get a `.wasm` file.
- **The world folder is the source of truth.** Copy a plugin folder into
another world, and it works there.
- **The agent is a normal plugin.** It can read and upgrade its own source,
and change how it does inference.
## Interfaces
- **Generated imports and interface checks, without version numbers.** A
model that translates a description by hand makes errors. A generator does
not. The check catches the most common error in a world that changes
itself: a change to one plugin breaks another one.
- **A small schema subset.** With Draft 7 and a fixed subset, our own check
can decide every case. It never has to guess.
- **Docs are part of the interface.** Other agents work from them, so the
SDK requires a doc comment on each method, type, and field.
- **The check covers calls, not state.** Old and new versions share
`/state`. A plugin keeps its state format compatible, or migrates it in
`rpc.retired`.
## Minimal on purpose
The core does only what a plugin cannot do for itself. Everything else can be
a plugin, or a change that the agent makes to itself.
- **No permissions, no limits, no hiding.** Any process can call, upgrade,
or stop any plugin, read and write every file, and use the network. The
checks protect against mistakes, not against plugins. Locks are advisory.
- **Locks are in the core.** A plugin cannot see that another process died,
so it cannot release that process's lock. The core can.
- **Our own VFS, in its own crate.** It works on Linux and macOS, it makes
`/proc` live, and plugin-served files need it later. Mounts now, overlays
later.
- **TCP endpoints for now.** They need no host code. VFS pipes can replace
them later behind the same SDK API.
- **Simple scheduling for now.** An epoch tick every 10 milliseconds keeps
the core responsive and makes kills reliable.
- **The agent is dumb.** It has a short prompt, a few tools, an append-only
history that it sends in full, and no compaction. When the context is too
large, it stops sending the oldest turns, so it never gets stuck.
## Small choices
- **Plain POSIX files.** Programs like `tempfile`, git, and SQLite expect
normal files. Atomic writes use a temporary file and `rename`.
- **Tests run under WASI.** `cargo test` on the host cannot find failures
that occur only in WASI, for example a missing WASI function or a panic at
start. The test runner uses the same Wasmtime and the same mounts as the
core.
- **The SDK is not published.** `cargo` points `sausage-plugin = "0.1"` at
its own copy, which it also shows in `/plugins/cargo/sdk/`.
- **Compiled code is a cache, not state.** It lives outside the world, in
one cache folder that all worlds share: `wasmtime/` (owned by Wasmtime,
whose cleanup deletes unknown entries), `cargo-target/`, and
`sdk/<hash>/`. A path that depends only on the content of the SDK lets
cargo reuse the compiled SDK in every world. Tests run in the release
profile, so that they share compiled crates with the release build.
- **Discovery has a deadline of 10 seconds.** It is an internal call of the
core, not a limit on plugins. Without it, one plugin that does not answer
would block every activation.
- **A broken plugin blocks nobody.** A plugin without a document is already
broken, so the check cannot protect it. The graph shows it as
"unavailable", and `core.revert` and `force` always work.
- **`rpc.retired` is best effort.** It does not run if the host stops first.
A persistent queue of retirements would be more core for a rare case, so a
new version checks its own migration marker instead.