Files
boxmaker/docs/plans/M2b/02-loopd-config.md
kyleandClaude Fable 5.1 e156975649 Add M2b plan: ten tasks, tests, recordings and the first system prompt
The tasks build the agent loop on M2a's client: channel messages and
the usage record in proto, four config tables, the tool port and
registry with find_tool and call_tool, the baseline and replay, the
session store, the turn loop with its limits and the append-only
property test, the channel server, loopd serve, bxctl chat, and the
device checks including a four-turn conversation with a restart.

Checked against a private reference implementation: the gate passes
after every task in order, the new suites pass under CPU load, and the
reference passes make verify-device on straylight with no cache loss.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 17:19:02 -07:00

82 lines
3.1 KiB
Markdown

# M2b task 02: the M2b config tables
**Branch:** `m2b` (run `git switch m2b`; `git status --short` must be empty, otherwise stop)
**Commit subject:** `Add the paths, channel, loop and baseline config tables`
## Goal
Four small config tables that the rest of M2b reads. All are optional, with defaults, and reject
unknown keys like the existing ones.
```toml
[paths]
home = "/var/lib/boxmaker" # default: $BOXMAKER_HOME if set, else this
[channel]
socket = "" # default empty, meaning <home>/run/loop/loop.sock
[loop]
tool_iterations = 8 # completions with tool calls allowed in one turn
repeat_detection = true # stop the same call made twice in a turn
tool_result_cap = 16384 # bytes of a tool result that are kept
[baseline]
system = "system.md" # the system prompt; relative to the config file's directory
```
## Files
- Copy (replacing the old one): `crates/loopd/tests/config.rs`;
new: `crates/loopd/tests/fixtures/config/m2b.toml`
- Modify: `crates/loopd/src/config.rs`, `docs/implementer-log.md`
## Interfaces
Added to `Config` (all `pub`, all `#[serde(default)]`): `paths: Paths`, `channel: Channel`,
`r#loop: Loop`, `baseline: Baseline`. The field is spelled `r#loop` because `loop` is a keyword;
in TOML it is `[loop]`.
```rust
pub struct Paths { pub home: PathBuf } // Default: $BOXMAKER_HOME or /var/lib/boxmaker
pub struct Channel { pub socket: PathBuf } // Default: empty
pub struct Loop { pub tool_iterations: u32, pub repeat_detection: bool, pub tool_result_cap: usize }
pub struct Baseline { pub system: PathBuf } // Default: "system.md"
impl Config {
/// The channel socket, with the default filled in.
pub fn channel_socket(&self) -> PathBuf;
}
```
Derives: `Debug, Clone, PartialEq, Eq, Deserialize` and `#[serde(deny_unknown_fields, default)]`
on all four, each with a hand-written `Default` (or derived, for `Channel`).
Rule: `Config::load` makes a relative `baseline.system` absolute by joining it to the config
file's directory. `Config::parse` leaves it as it is, because it has no file to be relative to.
## Steps
- [ ] **1. Copy.**
```sh
git switch m2b
cp docs/plans/M2b/files/crates/loopd/tests/config.rs crates/loopd/tests/
cp docs/plans/M2b/files/crates/loopd/tests/fixtures/config/m2b.toml crates/loopd/tests/fixtures/config/
```
- [ ] **2. See the tests fail.** `cargo test -p loopd --test config`. Expected: it does not compile.
- [ ] **3. Make the changes.** Run `cargo fmt --all`.
- [ ] **4. See the tests pass.** `cargo test -p loopd --test config`. Expected: `9 passed`.
- [ ] **5. Run the gate.** `make gate`. Expected last line: `gate: ok`.
- [ ] **6. Log and commit.** `git add crates/loopd docs/implementer-log.md && git commit`
## Done when
- `cargo test -p loopd --test config` reports 9 passed; `make gate` prints `gate: ok`.
- `grep -c deny_unknown_fields crates/loopd/src/config.rs` prints 10 (the six old structs and
the four new ones).
## Stop and report if
- `r#loop` cannot be used as a field name with serde. (It can; `serde` strips the `r#`.)