Files
sausage/AGENTS.md
T
surma d44f1d9677 Build separate native and WASI pipelines with shared multicall names
Checkpoint before integration and package validation.

Assisted-by: SurmAgent:unknown [read] [edit] [bash]
2026-11-12 09:36:27 +00:00

89 lines
4.3 KiB
Markdown

# Working on Sausage (v3)
Sausage is a world of WASI programs and durable agents. It is meant to be read and changed by agents,
including its own. Keep it that way.
This branch (`v3`) rebuilds Sausage. The plan and its decisions (D1, D2, …) are in
`~/research/sausage-v3/PLAN.md`. `README.md` describes the current build and layout.
## The boundary
- **The world is the sandbox.** Everything inside the world sees and changes the whole world. Do not add
inner boundaries, permissions or per-agent views. The VFS is a safety net against mistakes, not a wall
against attackers.
- **Host calls pierce the sandbox.** The only host import beyond standard WASI is the process interface
(spawn, wait, kill). Do not add another one. Everything else uses standard WASI 0.2 interfaces: files
through the VFS, `wasi:http` (the host API on its reserved host name, credential injection, signing),
and `wasi:sockets`.
- **The credentials manager in the host is the one real security boundary.** Its managed secrets never enter
the world. Approvals need a passkey. The one exception is the LLM proxy key in `/etc/sausage/config.yaml`.
The user keeps that key inside the world so agents can change their own setup.
- **Native code is the exception.** The only native work for the world is the Rust builder, and it runs in
user, mount and network namespaces.
## Priorities
1. **Correct.** Every behavior has a test.
2. **Simple and short.** Prefer the obvious code. No abstraction for one caller. No configuration that
nobody asked for.
3. **Readable.** A new reader must understand a file without the others. Each module starts with a doc
comment that says what it owns.
4. **Efficient** on the paths that run per command and per model turn.
## Layout
- `host/`: the host crate (package and binary `sausage`): the world, the process table, the harness.
`host/src/exec.rs` is the contract between the harness and the process table.
- `vfs/`: `sausage-vfs`, the virtual filesystem for WASI guests and for host code. It knows nothing
about Sausage. Host code reads and writes the world only through it.
- `userland/`: the default CLIs of a world, built for `wasm32-wasip2` in the root Cargo workspace.
The patched Brush crates live in `userland/brush/source/` and share the root lock file.
- `spikes/`: accepted prototypes. Read them for evidence, copy from them, do not build on them.
## Programs in the world
- Agents write CLIs and servers. That they are WASI binaries is a detail, not a concept to explain.
- The platform is WASI 0.2 components. Rust programs build for `wasm32-wasip2`. WASI 0.1 programs run
through an adapter.
## Checks
```sh
nix develop -c cargo test -p sausage -p sausage-vfs
nix develop -c cargo clippy -p sausage -p sausage-vfs --all-targets -- -D warnings
nix develop -c nu userland/bundle.nu
# For one tool, select its package and WASI target explicitly:
nix develop -c cargo build -p git --target wasm32-wasip2 --release
```
`nix build` uses separate Crane pipelines for the native host and WASI userland.
Go tools and QuickJS build separately. Nix assembles the bundle without `bundle.nu`.
Both Nix and the incremental development bundle read the command names from `userland/tools.json`.
Coreutils, diffutils, procps, and brush use relative symlinks to one binary each.
`nix flake check` checks the packaged host, its bundle, and the whole host/VFS test suite.
Sandbox-incompatible tests print an explicit skip reason instead of a flake-level filter.
## Tests
Host integration tests share one executable:
```sh
cargo test -p sausage --test it
cargo test -p sausage --test it -- chain::
```
Use the required Nix environment and build slot wrapper for these commands.
Add test modules under `host/tests/it/` and declare them in `it/main.rs`.
Use fully qualified child test filters with `--exact`.
See `host/tests/README.md` for fixture and isolation details.
Tests must stay fast:
- Only a few tests may wait for a timing (grace periods, deadlines).
- Poll for a condition with a deadline. Do not sleep.
- Do not test unrealistic load, such as many fast cycles.
- `#[ignore]` only disables a test for a short time, with the reason in the attribute.
- Tests that build or compile guests use a persistent cache (for example under
`env!("CARGO_TARGET_TMPDIR")`), so a warm run does not compile again.