Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
367 lines
21 KiB
Markdown
367 lines
21 KiB
Markdown
# M5c-1 task 10: `memoryd`'s Honcho clients, and making sure a workspace, its peers and a session exist
|
|
|
|
**Branch:** `m5c1` (run `git switch m5c1`; `git status --short` must be empty, otherwise stop)
|
|
**Commit subject:** `memoryd: a Honcho client per workspace, and its workspace, peers and sessions made sure of`
|
|
**Log row:** the Task cell is `M5c1/10-memoryd-honcho-client`.
|
|
|
|
## Goal
|
|
|
|
Tasks 11 and 13 send turns, conclusions and fleet messages to Honcho. This task gives them what
|
|
they send through (M5c spec, sections 3 and 6):
|
|
|
|
- **A client per workspace.** One `roncho` blocking client for each of this agent's three
|
|
workspaces (`[honcho] trusted`, `untrusted`, `fleet`), each with its own key when
|
|
`[secrets.honcho_trusted]` (and so on) is configured. Every client is built with
|
|
`.without_config_file()`: without it `roncho` reads `~/.config/roncho/roncho.toml` and takes an
|
|
`api_key` from it. Timeouts: connect 5 s, read ("no byte for") 30 s; chat, which M5c-2 uses, 180
|
|
s (the constant only, here).
|
|
- **Making sure things exist** before anything is written: the workspace, the agent's peer, the
|
|
speakers' peers, then the session. Each is Honcho's get-or-create (`POST`: 201 when made, 200
|
|
when it was there; both are success, and so is a 409). A session must be made before a message
|
|
is added to it: Honcho makes a missing one on the fly, with no peers configured (recorded:
|
|
`20-messages-add-missing-session.json`).
|
|
- The fleet workspace is made with `reasoning`, `summary` and `dream` all `enabled: false`; the
|
|
other two with Honcho's defaults (no configuration sent).
|
|
- The agent's peer is made with `observe_me: false`; a speaker's peer with nothing.
|
|
- A session holds the agent's peer (`observe_me: false, observe_others: false`) and each
|
|
speaker's (`observe_me: true, observe_others: false`).
|
|
- What was made sure of is remembered in memory for the run. After a restart everything is made
|
|
sure of again, which costs one request each and changes nothing.
|
|
- **Which Honcho session a turn goes to** (spec 3): the Boxmaker session's id; a speaker new to it
|
|
starts the next, `<id>-2`, `<id>-3`, holding every speaker so far (a Honcho session's peers are
|
|
fixed when it is made). The numbering comes from the ledger alone: the speakers of that
|
|
session's decided `Turn` actions, in any workspace, in the order of the decisions, each counted
|
|
once, up to and including the turn. A restart numbers the same.
|
|
- **Errors by kind and status, never a body.** A 422 echoes the whole request, memory text
|
|
included, and `roncho`'s `Status` and `Decode` errors carry up to 4 KiB of the response. So a
|
|
`HonchoError` keeps only a kind (a fixed word) and a status.
|
|
- **Deleting a conclusion** (for task 13, which uses it): 204 is done, and 404 (deleted before) is
|
|
done too.
|
|
|
|
Nothing calls this yet: task 11 builds the clients at start and sends turns. No request goes to
|
|
a real Honcho in any test: a fake Honcho on `127.0.0.1` answers from the responses the design
|
|
model recorded from rift in task 00, `crates/memoryd/tests/fixtures/honcho/`, which this task
|
|
copies in.
|
|
|
|
## Interfaces
|
|
|
|
In `crates/memoryd/src/honcho/client.rs` (the module `memoryd::honcho::client`):
|
|
|
|
```rust
|
|
pub const CONNECT: Duration = Duration::from_secs(5);
|
|
pub const READ: Duration = Duration::from_secs(30);
|
|
pub const CHAT_READ: Duration = Duration::from_secs(180);
|
|
pub struct Timeouts { pub connect: Duration, pub read: Duration } // Timeouts::WRITES = (CONNECT, READ)
|
|
pub enum HonchoError {
|
|
Unreachable { kind: &'static str, status: Option<u16> },
|
|
Refused { kind: &'static str, status: Option<u16> },
|
|
KeyRefused { status: u16 },
|
|
}
|
|
impl HonchoError { pub fn kind(&self) -> &'static str; pub fn status(&self) -> Option<u16>; }
|
|
pub fn classify(error: &roncho::blocking::Error) -> HonchoError;
|
|
pub fn fleet_configuration() -> serde_json::Map<String, Value>;
|
|
pub fn session_peers(agent: &str, speakers: &[String]) -> HashMap<String, SessionPeerConfig>;
|
|
pub struct Clients { /* private */ }
|
|
impl Clients {
|
|
pub fn new(config: &HonchoConfig, env: &dyn Fn(&str) -> Option<OsString>, timeouts: Timeouts)
|
|
-> Result<(Clients, Vec<String>), String>;
|
|
pub fn client(&self, workspace: Workspace) -> &roncho::blocking::Client;
|
|
pub fn ensure_workspace(&mut self, workspace: Workspace) -> Result<(), HonchoError>;
|
|
pub fn ensure_peer(&mut self, workspace: Workspace, peer: &str) -> Result<(), HonchoError>;
|
|
pub fn ensure_session(&mut self, workspace: Workspace, session: &HonchoSession)
|
|
-> Result<(), HonchoError>;
|
|
pub fn delete_conclusion(&self, workspace: Workspace, id: &str) -> Result<(), HonchoError>;
|
|
}
|
|
```
|
|
|
|
In `crates/memoryd/src/honcho/naming.rs` (the module `memoryd::honcho::naming`):
|
|
|
|
```rust
|
|
pub struct HonchoSession { pub name: String, pub speakers: Vec<String> }
|
|
pub fn session_name(session: &SessionId, count: usize) -> String;
|
|
pub fn honcho_session<'a, I>(decisions: I, session: &SessionId, turn: u64) -> Option<HonchoSession>
|
|
where I: IntoIterator<Item = (&'a Item, &'a Action)>;
|
|
```
|
|
|
|
In `crates/memoryd/src/honcho/ledger.rs`, one new method (edit L1):
|
|
`pub fn decisions(&self) -> impl Iterator<Item = (&Item, &Action)> + '_`, every decided item and
|
|
its action (`Skip` included) in the order of the `Decided` records.
|
|
|
|
`Clients::new` returns the key files' plaintext warnings for the caller to log. Its error is one
|
|
line naming the key (`bxcore::secrets`' own text, with its pointer) or the workspace; never a
|
|
key's value.
|
|
|
|
## The errors
|
|
|
|
`classify` is given whole in the skeleton. It maps `roncho`'s error so:
|
|
|
|
| `roncho::blocking::Error` | `HonchoError` |
|
|
|---|---|
|
|
| `Connect`, `Timeout`, `Closed`, `Io` | `Unreachable`, kind `connect`, `timeout`, `closed`, `io`; no status |
|
|
| `Status` with a status of 500 or more | `Unreachable`, kind `server`, the status |
|
|
| `Status` below 500 | `Refused`, kind `status`, the status |
|
|
| `Unauthorized` (401 or 403) | `KeyRefused`, the status |
|
|
| `Redirect` | `Refused`, kind `redirect`, the status |
|
|
| `ActiveSessions` | `Refused`, kind `active-sessions`, 409 |
|
|
| `TooLarge`, `Decode`, `Stream`, `Config`, any other | `Refused`, kind `too-large`, `decode`, `stream`, `config`, `other`; no status |
|
|
|
|
`Display` (given): `Honcho is unreachable (<kind>[, HTTP <status>])`, `Honcho refused the request
|
|
(<kind>[, HTTP <status>])`, `Honcho refused the key (key-refused, HTTP <status>)`. For a create,
|
|
a `Refused` with status 409 is success (`created`); for a delete, one with 404 is.
|
|
|
|
## The requests
|
|
|
|
Exactly what the given tests expect (`Value` bodies, compared as JSON):
|
|
|
|
| Step | Request | Body |
|
|
|---|---|---|
|
|
| a workspace | `POST /v3/workspaces` | `{"id":"<name>"}`; the fleet: `{"id":"<name>","configuration":{"reasoning":{"enabled":false},"summary":{"enabled":false},"dream":{"enabled":false}}}` |
|
|
| the agent's peer | `POST /v3/workspaces/<name>/peers` | `{"id":"<agent>","configuration":{"observe_me":false}}` |
|
|
| a speaker's peer | the same | `{"id":"<speaker>"}` |
|
|
| a session | `POST /v3/workspaces/<name>/sessions` | `{"id":"<session>","peers":{"<agent>":{"observe_me":false,"observe_others":false},"<speaker>":{"observe_me":true,"observe_others":false},…}}` |
|
|
| delete a conclusion | `DELETE /v3/workspaces/<name>/conclusions/<id>` | none |
|
|
|
|
`ensure_session` (given whole) makes sure of the workspace, the agent's peer, each speaker's peer,
|
|
and then the session, each only if not yet made sure of in this run. A failed step is not
|
|
remembered, so the next call tries it again.
|
|
|
|
## Every place that changes
|
|
|
|
This is the complete list.
|
|
|
|
| Place | What happens there |
|
|
|---|---|
|
|
| `crates/memoryd/src/honcho/mod.rs` | `pub mod client;` and `pub mod naming;` (edit H1) |
|
|
| `crates/memoryd/src/honcho/ledger.rs` | `decisions` (edit L1) |
|
|
| `crates/memoryd/src/honcho/client.rs` | new: a skeleton you copy, then fill 11 bodies |
|
|
| `crates/memoryd/src/honcho/naming.rs` | new: a skeleton you copy, then fill 2 bodies |
|
|
| `crates/memoryd/tests/fixtures/honcho/` | new: the recorded responses, copied |
|
|
| everything else (`serve.rs`, `turns.rs`, `config.rs`, `Cargo.toml`, other crates) | **no change** |
|
|
|
|
## Files
|
|
|
|
(A copy command names `<file>-taskNN` where a later task gives a newer version of that file:
|
|
this task's version is the one named.)
|
|
|
|
- Copy, from `docs/plans/M5c1/files/` (the same path under it):
|
|
- new tests: `crates/memoryd/tests/honcho_client.rs`, `crates/memoryd/tests/honcho_keys.rs`,
|
|
`crates/memoryd/tests/honcho_naming.rs`
|
|
- new test support: `crates/memoryd/tests/support/honcho.rs` (the fake Honcho), from
|
|
`honcho.rs-task10` (this task's version; task 13 gives a later one)
|
|
- the directory `crates/memoryd/tests/fixtures/honcho/` (29 JSON files and a README), whole
|
|
- the skeletons `crates/memoryd/src/honcho/client.rs` and `crates/memoryd/src/honcho/naming.rs`
|
|
- Modify: `crates/memoryd/src/honcho/mod.rs`, `crates/memoryd/src/honcho/ledger.rs`,
|
|
`docs/implementer-log.md`.
|
|
- Do not touch: any other source file, any other test or support file, the fixtures (never edit
|
|
one, and never record new ones: there is no Honcho to reach from here), any `Cargo.toml`, and in
|
|
the skeletons anything but the 13 `todo!()` bodies.
|
|
|
|
## Facts you need
|
|
|
|
- **`roncho` 1.0.0** is already a dependency (`roncho.workspace = true`, the feature `tls`, which
|
|
brings `blocking`). Its blocking client is HTTP/1.1 over `std::net`, one request per
|
|
connection. The calls this task uses, all on `roncho::blocking::Client`:
|
|
|
|
| Call | Signature, and what it gives |
|
|
|---|---|
|
|
| `Client::builder()` | `-> ClientBuilder`; then `.without_config_file()`, `.base_url(url)`, `.workspace_id(name)`, `.connect_timeout(d)`, `.read_timeout(d)`, `.api_key(key)` (each `self -> Self`), `.build() -> Result<Client, Error>` |
|
|
| `workspace_id` | `client.workspace_id() -> &str` |
|
|
| `get_or_create_workspace` | `client.get_or_create_workspace(&WorkspaceCreate) -> Result<roncho::Workspace, Error>` |
|
|
| `peer` | `client.peer(&PeerCreate) -> Result<roncho::models::Peer, Error>` |
|
|
| `session` | `client.session(&SessionCreate) -> Result<roncho::models::Session, Error>` |
|
|
| `delete_conclusion` | `client.delete_conclusion(id: &str) -> Result<(), Error>` |
|
|
| `WorkspaceCreate` | `roncho::WorkspaceCreate::new(id)`, `.with_configuration(Map<String, Value>)` |
|
|
| `PeerCreate` | `roncho::PeerCreate::new(id)`, `.with_configuration(Map<String, Value>)` |
|
|
| `SessionCreate` | `roncho::SessionCreate::new(id)`, `.with_peers(HashMap<String, SessionPeerConfig>)` |
|
|
| `SessionPeerConfig` | `roncho::SessionPeerConfig::new().observe_me(bool).observe_others(bool)` |
|
|
|
|
`roncho`'s `Client` and `ClientBuilder` `Debug` show `api_key_present`, never the key; the
|
|
client keeps the key in a `Zeroizing<String>`.
|
|
- **Keys.** `HonchoConfig.keys` (task 06) maps `honcho_trusted`, `honcho_untrusted`,
|
|
`honcho_fleet` to a checked `SecretSource`. `bxcore::secrets::load("memoryd", name, source,
|
|
env) -> Result<Loaded, SecretError>` reads it (`Loaded { secret: Secret, warning:
|
|
Option<String> }`; a file source always has a warning). `Secret::expose() -> &str` is the one
|
|
place the value is taken, to hand it to `roncho`. `SecretError`'s `Display` names the key and
|
|
ends with `see docs/runbook.md#secret-unavailable`. `env` is `std::env::var_os` in `memoryd`;
|
|
tests pass their own.
|
|
- **serde_json.** `serde_json::Map::new()`, `map.insert(String, Value)`, and
|
|
`serde_json::json!({ "enabled": false })` (imported as `json`).
|
|
- **The fake Honcho** (`tests/support/honcho.rs`) answers by method and the shape of the path:
|
|
the first create of a thing with its 201 fixture, a repeat with its 200 one; the first delete
|
|
of a conclusion 204, a repeat 404; anything else 404. It can be set to answer every request with
|
|
one status (a 422 echoing the request's body, as Honcho does) or to answer nothing, and it
|
|
records every request with its headers and body.
|
|
- **The given tests.**
|
|
- `honcho_client.rs` (13 tests): the timeouts; each client's workspace and timeouts (from
|
|
`roncho`'s `Debug`); the fleet's configuration equal to the recorded one; the four requests of
|
|
a session, in order and exactly; nothing asked twice in a run, a new speaker's peer and
|
|
session only, another workspace on its own; everything asked again after a restart (200 is
|
|
success); the fleet's workspace and a session with the agent alone; 409 on create; errors
|
|
by kind and status, not remembered, and a timeout (the fake answers nothing, the read timeout
|
|
is 300 ms); nothing listening; no response text in an error; `classify` for every error;
|
|
deleting a conclusion twice.
|
|
- `honcho_keys.rs` (3 tests, and 1 ignored that the third runs): each key sent as a bearer
|
|
token to its own workspace only, and shown by no client, error or warning; a key that cannot
|
|
be read; a `roncho.toml` with an `api_key` under `HOME` not read (a child process of the test
|
|
binary, since `HOME` is the process's).
|
|
- `honcho_naming.rs` (5 tests): the names; each new speaker, in any workspace, starting the next
|
|
session; a skipped or undecided turn; the order of decision; a restart; `decisions`.
|
|
- **Where the delete lives.** `delete_conclusion` is here, with its two recorded answers; task 13
|
|
calls it for an undo.
|
|
|
|
## The edits, exactly
|
|
|
|
### `crates/memoryd/src/honcho/mod.rs`
|
|
|
|
**H1.** The lines
|
|
|
|
```rust
|
|
pub mod config;
|
|
pub mod decide;
|
|
pub mod ledger;
|
|
```
|
|
|
|
become
|
|
|
|
```rust
|
|
pub mod client;
|
|
pub mod config;
|
|
pub mod decide;
|
|
pub mod ledger;
|
|
pub mod naming;
|
|
```
|
|
|
|
### `crates/memoryd/src/honcho/ledger.rs`
|
|
|
|
**L1.** The lines
|
|
|
|
```rust
|
|
/// The items waiting to be sent, with their actions, in the order they were decided.
|
|
pub fn outbox(&self) -> Vec<(Item, Action)> {
|
|
```
|
|
|
|
become
|
|
|
|
```rust
|
|
/// Every decided item with its action, `Skip` included, in the order they were decided.
|
|
pub fn decisions(&self) -> impl Iterator<Item = (&Item, &Action)> + '_ {
|
|
self.order
|
|
.iter()
|
|
.filter_map(|item| Some((item, &self.entries.get(item)?.action)))
|
|
}
|
|
|
|
/// The items waiting to be sent, with their actions, in the order they were decided.
|
|
pub fn outbox(&self) -> Vec<(Item, Action)> {
|
|
```
|
|
|
|
## Steps
|
|
|
|
- [ ] **1. Copy the given files.** From the repository root:
|
|
|
|
```sh
|
|
mkdir -p crates/memoryd/tests/fixtures
|
|
cp -r docs/plans/M5c1/files/crates/memoryd/tests/fixtures/honcho crates/memoryd/tests/fixtures/
|
|
cp docs/plans/M5c1/files/crates/memoryd/tests/honcho_client.rs crates/memoryd/tests/
|
|
cp docs/plans/M5c1/files/crates/memoryd/tests/honcho_keys.rs crates/memoryd/tests/
|
|
cp docs/plans/M5c1/files/crates/memoryd/tests/honcho_naming.rs-task10 crates/memoryd/tests/honcho_naming.rs
|
|
cp docs/plans/M5c1/files/crates/memoryd/tests/support/honcho.rs-task10 crates/memoryd/tests/support/honcho.rs
|
|
```
|
|
|
|
`ls crates/memoryd/tests/fixtures/honcho | wc -l` prints `30`.
|
|
- [ ] **2. See them fail.** `cargo build --tests --keep-going -p memoryd`, with a shell timeout
|
|
of 600000 ms: three test targets do not compile. `honcho_client`, 2 errors: `E0432`
|
|
(``unresolved import `memoryd::honcho::client` ``) and `E0432` (the same for
|
|
`memoryd::honcho::naming`). `honcho_keys`, 1 error: the `E0432` for `client`. `honcho_naming`,
|
|
8 errors: the `E0432` for `naming`, and seven `E0599` (``no method named `decisions` found for
|
|
struct `memoryd::honcho::ledger::Ledger` ``). Every other target compiles.
|
|
- [ ] **3. The modules.** Make edits H1 and L1, then copy the two skeletons:
|
|
|
|
```sh
|
|
cp docs/plans/M5c1/files/crates/memoryd/src/honcho/client.rs crates/memoryd/src/honcho/
|
|
cp docs/plans/M5c1/files/crates/memoryd/src/honcho/naming.rs crates/memoryd/src/honcho/
|
|
```
|
|
|
|
`cargo check -p memoryd`: no error, and 26 warnings, all from the unfilled bodies: ``unused
|
|
import: `json` ``; ``unused imports: `PeerCreate` and `WorkspaceCreate` ``; twenty-two `unused
|
|
variable` (`workspace` five times, `session` twice, and `agent`, `config`, `count`,
|
|
`decisions`, `env`, `id`, `key`, `name`, `peer`, `result`, `speakers`, `timeouts`, `turn`,
|
|
`url`, `warnings` once each); ``fields `trusted`, `untrusted`, and `fleet` are never read``;
|
|
and ``variants `Workspace` and `Peer` are never constructed``. They go as you fill the bodies.
|
|
- [ ] **4. Fill the thirteen bodies, one at a time, in the order of each file**, each from its
|
|
comment, and run `cargo check -p memoryd` after each. In `client.rs`: `kind`, `status`,
|
|
`fleet_configuration`, `session_peers`, `created`, `client` (the free function), `key`,
|
|
`Clients::client`, `ensure_workspace`, `ensure_peer`, `delete_conclusion`. In `naming.rs`:
|
|
`session_name`, `honcho_session`. When a body is filled, delete its instruction comment (the
|
|
`//` lines inside the body) and keep the function's doc comment (`///`) as it is. Change
|
|
nothing else: `classify`, `Display`, `Clients::new` and `ensure_session` are given. After the
|
|
last, `cargo check -p memoryd` prints no warning.
|
|
- [ ] **5. The tests.** With a shell timeout of 600000 ms, and every test passes (13, 3 and 5; one
|
|
test is ignored: the child the third test of `honcho_keys` runs):
|
|
|
|
```sh
|
|
cargo test -p memoryd --test honcho_client --test honcho_keys --test honcho_naming
|
|
```
|
|
- [ ] **6. Check.** Three commands:
|
|
- `grep -c 'without_config_file' crates/memoryd/src/honcho/client.rs` prints `1`.
|
|
- `grep -n 'expose()' crates/memoryd/src -r` prints exactly one line, in `client` (the free
|
|
function): the one place a key's value is taken.
|
|
- `git diff --stat -- crates/bxcore crates/loopd crates/brokerd crates/gatewayd crates/bxctl
|
|
Cargo.toml Cargo.lock crates/memoryd/Cargo.toml crates/memoryd/src/serve.rs` prints nothing.
|
|
- [ ] **7. Run the gate.** `cargo fmt --all`, then `make gate`, with a shell timeout of 600000 ms.
|
|
Expected last line: `gate: ok`, with 1278 tests: 1257 before this task, plus 21 new ones (13 in
|
|
`honcho_client.rs`, 3 in `honcho_keys.rs`, 5 in `honcho_naming.rs`).
|
|
- [ ] **8. Log and commit.** Add your row to `docs/implementer-log.md`; its Task cell is
|
|
`M5c1/10-memoryd-honcho-client`. `git status --short` lists `honcho/mod.rs`, `honcho/ledger.rs`
|
|
and your log as modified, and `honcho/client.rs`, `honcho/naming.rs`, the three test files,
|
|
`tests/support/honcho.rs` and `tests/fixtures/` as new. Stage with
|
|
`git add crates/memoryd docs/implementer-log.md Cargo.lock`, then commit as `AGENTS.md` says.
|
|
`Cargo.lock` does not change in this task; the `git add` is harmless.
|
|
|
|
## Done when
|
|
|
|
- `make gate` prints `gate: ok` with 1278 tests.
|
|
- `git diff HEAD~1 --stat` lists only the files of step 8.
|
|
|
|
## Stop and report if
|
|
|
|
- A "before" block of H1 or L1 is not in its file exactly as shown, or is there more than once.
|
|
- Step 1 does not give 30 files, or step 2 or 3 shows a different result: other test targets,
|
|
errors of another kind, or other warnings.
|
|
- `cargo check` reports an error after a body you filled, and the comment does not tell you how
|
|
to fix it. Quote the error, and stop.
|
|
- A comment in a skeleton does not fit the code under it, or disagrees with this task's tables.
|
|
Quote both and stop.
|
|
- After step 4 a test fails although each body follows its comment. Do not change a test, the
|
|
fake, a fixture or the skeletons' given code: stop, and give the failing tests' names and their
|
|
messages.
|
|
- A test seems to need a fixture that is not there, or a request to a real Honcho. It does not;
|
|
stop and say why you think so. Never record a response.
|
|
- The fix seems to need a change anywhere else: `serve.rs`, `turns.rs`, a `Cargo.toml`, another
|
|
crate. It does not; stop and say why you think so.
|
|
- A test outside the new files fails in the gate: check once whether it fails without your
|
|
change, then stop and report both results.
|
|
- The gate passes with a count other than 1278.
|
|
|
|
## What the given tests pin
|
|
|
|
Each of these changes to the finished code makes at least one given test fail (44 tried, one
|
|
cannot change behaviour): either timeout ignored or changed; `roncho.toml` read; each error
|
|
mapped to the other side (a 5xx refused, a 401 refused, a timeout, a connect failure or a closed
|
|
socket refused, a bad body unreachable); a 409 on create an error, or any refusal on create
|
|
taken as success; a 404 on delete an error; the fleet's settings on another workspace, missing
|
|
one, or on; the agent observed, or every peer unobserved; the workspace, a peer or a session not
|
|
remembered, or remembered for every workspace, or before it was made; the session made without
|
|
its workspace, its agent or its speakers first; the agent observed or left out of a session, the
|
|
speakers unobserved or observing others; a key sent to the wrong workspace or dropped; a key
|
|
file's warning dropped; a key that cannot be read taken as no key; the wrong client for a
|
|
workspace; an error shown without its status; a session named `<id>-1`; a speaker counted
|
|
twice; another session's speakers counted; the speakers of later turns counted; `decisions`
|
|
without the skipped items or in reverse.
|
|
|
|
## After this task
|
|
|
|
Task 11 builds the `Clients` at start (after the ledger, logging the warnings, a key that cannot
|
|
be read stopping `memoryd` with `secret-unavailable`), and sends turns with `ensure_session` and
|
|
`honcho_session`. Task 13 uses `delete_conclusion`, and makes the fleet's sessions with no
|
|
speakers.
|