Files
boxmaker/docs/plans/M5c1/13-memoryd-honcho-edits.md
2026-09-28 09:58:26 -07:00

30 KiB

M5c-1 task 13: the Honcho worker sends memory writes

Branch: m5c1 (run git switch m5c1; git status --short must be empty, otherwise stop) Commit subject: memoryd: the Honcho worker sends conclusions, their deletions and fleet notes Log row: the Task cell is M5c1/13-memoryd-honcho-edits.

Goal

Task 12 decides each memory write into the ledger; task 11's worker sends only turns. This task makes the worker send the rest (M5c spec, sections 4 and 5): one outbox, oldest first, across every kind of item.

  • Conclude: the conclusion is made in the item's workspace (trusted), about its peer (observer and observed are both that peer), stamped with the Boxmaker session's memory session, <id>_memory. That Honcho session holds the agent's peer and the owner's and is made on first use, whether or not any turn of the session is synced (spec section 4, as of 2026-09-28). Neither peer observes in it: it holds conclusions, not messages, and the spec gives no flags for it, so both get the agent's (observe_me: false, observe_others: false). The text is neutralised as it leaves. Sent records the one id Honcho gives.
  • Unconclude: the id to delete is ledger.conclusion(workspace, text) (task 07): the latest sent Conclude of that text not yet undone. It is deleted (delete_conclusion, task 10: 204 or 404 is done), and Sent records the deleted id, which pops it from conclusion's stack. With none (never sent, its Conclude failed, or already undone) nothing is asked: Sent with no ids, and no log line. A Conclude still waiting cannot be behind its Unconclude: it was decided first, and the outbox keeps that order.
  • Fleet: the untrusted note is added to the fleet workspace (made with task 10's settings) as messages from the agent's peer, neutralised, in parts of 25,000 characters, in one batch, to the Honcho session named after the Boxmaker session, <id>, holding only the agent's peer (spec section 3). Sent records the ids.
  • Retract: nothing decides one any more (a note cannot be undone), so it waits in the outbox, untouched.
  • Errors, as for turns (task 11): unreachable waits and tries the same item again; refused marks it failed, logged once; a refused key stops that workspace. Log lines name an edit's item part <n> of edit <id>.

To stay under 500 lines, worker.rs's start code (Opened, open, spawn) moves, unchanged but for its imports, to a new file honcho/start.rs; worker.rs re-exports them, so serve.rs does not change.

Interfaces

In crates/memoryd/src/honcho/edits.rs (the module memoryd::honcho::edits):

pub const MEMORY_SUFFIX: &str = "_memory";
pub fn memory_session(session: &SessionId) -> String;
pub fn conclude(clients: &mut Clients, workspace: Workspace, peer: &str, session: &SessionId,
                text: &str) -> Result<Vec<String>, HonchoError>;
pub fn unconclude(clients: &Clients, workspace: Workspace, id: Option<&str>)
    -> Result<Vec<String>, HonchoError>;
pub fn fleet_batch(text: &str, agent: &str, session: &SessionId, edit: &str) -> Vec<MessageCreate>;
pub fn fleet(clients: &mut Clients, session: &SessionId, messages: &[MessageCreate])
    -> Result<Vec<String>, HonchoError>;

In client.rs: Clients keeps [honcho] owner, and gains ensure_memory_session(&mut self, workspace: Workspace, name: &str) -> Result<(), HonchoError> (edits C1 to C3). In worker.rs, the private Next holds a Job (Turn, Conclude, Unconclude, Fleet), next builds it for any kind of item, and send dispatches on it (edits W1 to W7).

The requests

Fixture 11 is the conclusion's shape:

Item Requests
Conclude make sure of the workspace, the agent's peer, the owner's peer and <id>_memory ({"id":"<id>_memory","peers":{"<agent>":{"observe_me":false,"observe_others":false},"<owner>":{"observe_me":false,"observe_others":false}}}), then POST /v3/workspaces/<trusted>/conclusions with {"conclusions":[{"content":"<text>","observer_id":"<peer>","observed_id":"<peer>","session_id":"<id>_memory"}]}
Unconclude DELETE /v3/workspaces/<trusted>/conclusions/<id>, or none
Fleet make sure of the fleet workspace, the agent's peer and <id> ({"id":"<id>","peers":{"<agent>":{"observe_me":false,"observe_others":false}}}), then POST /v3/workspaces/<fleet>/sessions/<id>/messages with each part {"content":"<part>","peer_id":"<agent>","metadata":{"agent":"<agent>","boxmaker_session":"<id>","edit":"<edit id>"}}

What was made sure of is remembered for the run (task 10), so a second conclusion in the same session asks only for the conclusion.

Every place that changes

This is the complete list.

Place What happens there
crates/memoryd/src/honcho/mod.rs pub mod edits; and pub mod start; (edits H1, H2)
crates/memoryd/src/honcho/client.rs owner, ensure_memory_session (edits C1 to C3)
crates/memoryd/src/honcho/edits.rs new: a skeleton you copy, then fill 5 bodies
crates/memoryd/src/honcho/start.rs new, given whole: the start code from worker.rs
crates/memoryd/src/honcho/worker.rs imports, Job and Next, label, the two log lines, next/send/send_turn, the start code removed (edits W1 to W7)
crates/memoryd/tests/support/honcho.rs a new version, given: the fake answers a conclusion batch (fixture 11), and a batch with as many items as it asked for
crates/memoryd/tests/honcho_worker_more.rs a new version, given: its "later actions wait" test is now about a retraction
everything else 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):
    • the new test crates/memoryd/tests/honcho_edits.rs
    • new versions (they replace the files in the tree) of crates/memoryd/tests/support/honcho.rs and crates/memoryd/tests/honcho_worker_more.rs; do not copy honcho.rs-task10 or honcho_worker_more.rs-task11, the older ones
    • the skeleton crates/memoryd/src/honcho/edits.rs and the file crates/memoryd/src/honcho/start.rs (given whole: nothing to fill)
  • Modify: the files of edits H1 to W7, docs/implementer-log.md.
  • Do not touch: any other source file (serve.rs in particular), any other test or support file, the fixtures, any Cargo.toml, and in the skeleton anything but the 5 todo!() bodies.

Facts you need

  • roncho's conclusion call (roncho::blocking::Client): create_conclusions(&ConclusionBatchCreate) -> Result<Vec<roncho::Conclusion>, Error> (POST .../conclusions; a batch of 1 to 100, checked before sending). ConclusionCreate::new(content, observer_id, observed_id), .with_session_id(name); ConclusionBatchCreate::new(vec![..]). Conclusion.id is the id. Honcho's src/schemas/api.py caps a conclusion batch at 100, as it does messages.
  • Task 07's conclusion replays the Sent records in order: a sent Conclude of a workspace and text pushes (item, ids[0]); a sent Unconclude of the same pops. So an Unconclude's Sent pops whether it carries the deleted id or none; the id is kept for the record.
  • Session names. _ is allowed in Honcho names and in no Boxmaker session id, so <id>_memory is never a turn session (<id>, <id>-2, …) or another session's id.
  • The given code of the worker, after W6: next matches (item, action) pairs; an Unconclude's id is looked up there, under the ledger's lock, when the item is picked; send calls send_turn (task 11's turn code, unchanged but for its arguments) or edits.
  • The given tests.
    • honcho_edits.rs (10 tests): the memory session's name; a conclusion's five requests, exactly, its id, and conclusion; a replace and its undo (the requests in order, the deleted ids, each thing made sure of once); an Unconclude with nothing to delete (never sent, or its Conclude failed); a 404 on delete; a fleet note's four requests, exactly, and a long note in parts; a note too long for one batch; neutralising; one outbox across a turn, an edit and a turn, and a restart that sends nothing; errors (an outage, a 422, a refused fleet key that leaves the trusted workspace going), with no text in a log line.
    • honcho_worker_more.rs (10 tests): task 11's, with its "later actions" test about a retraction, which waits.
  • The calls the bodies use:
    Call Signature, and what it gives
    Clients::ensure_memory_session edit C3: (&mut self, workspace, name: &str) -> Result<(), HonchoError>
    Clients::ensure_session, delete_conclusion, client task 10
    ConclusionCreate, ConclusionBatchCreate above
    Client::create_conclusions, add_messages above; task 11
    neutralise bxcore::sanitize::neutralise(text) -> Cow<'_, str>; ConclusionCreate::new takes it as impl Into<String>
    worker::parts task 11: parts of at most 25,000 characters
    classify task 10: roncho's error as a HonchoError

The edits, exactly

Nothing else in these files changes. Each quoted "before" block occurs exactly once in its file. Edit W6 replaces next and send as they are after task 12 (next's body is yours from task 11, with task 12's W5); edit W7 removes the start code, which moves to start.rs. If either "before" block differs from your file, stop and report.

crates/memoryd/src/honcho/worker.rs

W1. The lines

use crate::config::Config;
use crate::honcho::client::{Clients, HonchoError, Timeouts, classify};
use crate::honcho::config::HonchoConfig;
use crate::honcho::decide::{final_reply, turn_records, user_text};
use crate::honcho::ledger::{self, Action, Item, Ledger, Record, Workspace};
use crate::honcho::mirror::Mirror;
use crate::honcho::naming::{HonchoSession, honcho_session};
use crate::honcho::state;
use crate::honcho::turns::{LEDGER, TurnSync, log_path, read_log};
use crate::serve::{Log, StartError};

become

use crate::honcho::client::{Clients, HonchoError, classify};
use crate::honcho::decide::{final_reply, turn_records, user_text};
use crate::honcho::edits;
use crate::honcho::ledger::{self, Action, Item, Ledger, Record, Workspace};
use crate::honcho::naming::{HonchoSession, honcho_session};
use crate::honcho::turns::{TurnSync, log_path, read_log};
use crate::serve::Log;

pub use crate::honcho::start::{Opened, open, spawn};

W2. The lines

/// A turn to send: its item, workspace, speaker and Honcho session.
struct Next {
    item: Item,
    session: SessionId,
    turn: u64,
    workspace: Workspace,
    speaker: String,
    honcho: Option<HonchoSession>,
}

become

/// What to send for one waiting item (M5c spec, section 5).
enum Job {
    /// A turn: its session and number, its speaker, and its Honcho session.
    Turn {
        session: SessionId,
        turn: u64,
        speaker: String,
        honcho: Option<HonchoSession>,
    },
    /// A conclusion about `peer`, stamped with `session`'s memory session.
    Conclude {
        peer: String,
        session: SessionId,
        text: String,
    },
    /// Delete the conclusion with this id; `None` when no sent conclusion is live.
    Unconclude { id: Option<String> },
    /// An untrusted note: a fleet message from the agent.
    Fleet {
        session: SessionId,
        edit: String,
        text: String,
    },
}

/// The oldest waiting item: what it is, where it goes, and what to send.
struct Next {
    item: Item,
    workspace: Workspace,
    job: Job,
}

W3. The line

/// The ids of the messages Honcho made, in order.

becomes

/// An item as the log lines name it: `turn 3 of session mm-a`, `part 0 of edit 2026-09-28-0001`.
fn label(item: &Item) -> String {
    match item {
        Item::Turn { session, turn } => format!("turn {turn} of session {}", session.as_str()),
        Item::Edit { id, part } => format!("part {part} of edit {id}"),
    }
}

/// The ids of the messages Honcho made, in order.

W4. In step, in the arm Err(Unsent::Honcho(e)), the lines

                    "memoryd: turn {} of session {} was not sent: {e}; it is marked failed; {SEND_REFUSED}",
                    next.turn,
                    next.session.as_str()

become

                    "memoryd: {} was not sent: {e}; it is marked failed; {SEND_REFUSED}",
                    label(&next.item)

W5. In step, in the arm Err(Unsent::Local(kind)), the lines

                    "memoryd: turn {} of session {} cannot be sent ({kind}); it is marked failed; {pointer}",
                    next.turn,
                    next.session.as_str()

become

                    "memoryd: {} cannot be sent ({kind}); it is marked failed; {pointer}",
                    label(&next.item)

W6. The lines (the whole of next and send, from next's doc comment to the line before unreachable's)

    /// The oldest waiting turn whose workspace's key was not refused. Other actions wait.
    fn next(&self) -> Option<Next> {
        let ledger = self.ledger.lock().unwrap_or_else(|p| p.into_inner());
        ledger.outbox().into_iter().find_map(|(item, action)| {
            let Item::Turn { session, turn } = &item else {
                return None;
            };
            let Action::Turn { workspace, speaker } = action else {
                return None;
            };
            if self.refused.contains(&workspace) {
                return None;
            }
            Some(Next {
                honcho: honcho_session(ledger.decisions(), session, *turn),
                session: session.clone(),
                turn: *turn,
                item: item.clone(),
                workspace,
                speaker,
            })
        })
    }

    /// Read the turn again, make sure of its Honcho session, and add its messages as one batch.
    fn send(&mut self, next: &Next) -> Result<Vec<String>, Unsent> {
        let (text, reply) = read_turn(&log_path(&self.sessions, &next.session), next.turn)
            .ok_or(Unsent::Local("log"))?;
        let honcho = next.honcho.as_ref().ok_or(Unsent::Local("log"))?;
        let messages = batch(
            &text,
            &reply,
            &next.speaker,
            &self.agent,
            &next.session,
            next.turn,
        );
        if messages.len() > MAX_BATCH {
            return Err(Unsent::Local("too-long"));
        }
        self.clients
            .ensure_session(next.workspace, honcho)
            .map_err(Unsent::Honcho)?;
        let client = self.clients.client(next.workspace);
        match client.add_messages(&honcho.name, &messages) {
            Ok(made) => Ok(ids(&made)),
            Err(e) => Err(Unsent::Honcho(classify(&e))),
        }
    }

become

    /// The oldest waiting item whose workspace's key was not refused. A retraction, which
    /// nothing decides any more, waits.
    fn next(&self) -> Option<Next> {
        let ledger = self.ledger.lock().unwrap_or_else(|p| p.into_inner());
        ledger.outbox().into_iter().find_map(|(item, action)| {
            let (workspace, job) = match (&item, action) {
                (Item::Turn { session, turn }, Action::Turn { workspace, speaker }) => (
                    workspace,
                    Job::Turn {
                        honcho: honcho_session(ledger.decisions(), session, *turn),
                        session: session.clone(),
                        turn: *turn,
                        speaker,
                    },
                ),
                (
                    Item::Edit { .. },
                    Action::Conclude {
                        workspace,
                        peer,
                        session,
                        text,
                    },
                ) => (
                    workspace,
                    Job::Conclude {
                        peer,
                        session,
                        text,
                    },
                ),
                (Item::Edit { .. }, Action::Unconclude { workspace, text }) => (
                    workspace,
                    Job::Unconclude {
                        id: ledger.conclusion(workspace, &text).map(|(_, id)| id),
                    },
                ),
                (Item::Edit { id, .. }, Action::Fleet { session, text }) => (
                    Workspace::Fleet,
                    Job::Fleet {
                        session,
                        edit: id.clone(),
                        text,
                    },
                ),
                _ => return None,
            };
            if self.refused.contains(&workspace) {
                return None;
            }
            Some(Next {
                item: item.clone(),
                workspace,
                job,
            })
        })
    }

    /// Send one item, by what it is.
    fn send(&mut self, next: &Next) -> Result<Vec<String>, Unsent> {
        let honcho = |e| Unsent::Honcho(e);
        match &next.job {
            Job::Turn {
                session,
                turn,
                speaker,
                honcho: name,
            } => self.send_turn(next.workspace, session, *turn, speaker, name.as_ref()),
            Job::Conclude {
                peer,
                session,
                text,
            } => edits::conclude(&mut self.clients, next.workspace, peer, session, text)
                .map_err(honcho),
            Job::Unconclude { id } => {
                edits::unconclude(&self.clients, next.workspace, id.as_deref()).map_err(honcho)
            }
            Job::Fleet {
                session,
                edit,
                text,
            } => {
                let messages = edits::fleet_batch(text, &self.agent, session, edit);
                if messages.len() > MAX_BATCH {
                    return Err(Unsent::Local("too-long"));
                }
                edits::fleet(&mut self.clients, session, &messages).map_err(honcho)
            }
        }
    }

    /// Read the turn again, make sure of its Honcho session, and add its messages as one batch.
    fn send_turn(
        &mut self,
        workspace: Workspace,
        session: &SessionId,
        turn: u64,
        speaker: &str,
        honcho: Option<&HonchoSession>,
    ) -> Result<Vec<String>, Unsent> {
        let (text, reply) =
            read_turn(&log_path(&self.sessions, session), turn).ok_or(Unsent::Local("log"))?;
        let honcho = honcho.ok_or(Unsent::Local("log"))?;
        let messages = batch(&text, &reply, speaker, &self.agent, session, turn);
        if messages.len() > MAX_BATCH {
            return Err(Unsent::Local("too-long"));
        }
        self.clients
            .ensure_session(workspace, honcho)
            .map_err(Unsent::Honcho)?;
        let client = self.clients.client(workspace);
        match client.add_messages(&honcho.name, &messages) {
            Ok(made) => Ok(ids(&made)),
            Err(e) => Err(Unsent::Honcho(classify(&e))),
        }
    }

W7. At the end of the file, the lines (the end of impl Worker, then the start code)

}

/// With `[honcho]`, at start: the ledger read through, the sessions that wait again, and the
/// clients with their keys read. Nothing is bound or started yet.
pub struct Opened {
    pub sync: Arc<Mutex<TurnSync>>,
    clients: Clients,
    agent: String,
    owner: String,
}

impl Opened {
    /// The memory writes' side of the ledger (task 12).
    pub fn mirror(&self) -> Mirror {
        let ledger = self
            .sync
            .lock()
            .unwrap_or_else(|p| p.into_inner())
            .shared_ledger();
        Mirror::new(ledger, &self.agent, &self.owner)
    }
}

/// Open the ledger (a cut-off last line is logged), resume the waiting sessions, and build the
/// clients (their key files' warnings are logged). A key that cannot be read is `Secret`.
pub fn open(config: &Config, honcho: &HonchoConfig, log: &Log) -> Result<Opened, StartError> {
    let path = config.memory_dir().join(LEDGER);
    let ledger = Ledger::open(&path).map_err(StartError::Ledger)?;
    if ledger.repaired() {
        (log)(&format!(
            "memoryd: {}: a last line cut off by a crash was dropped",
            path.display()
        ));
    }
    let mut sync = TurnSync::new(ledger, &honcho.agent, &config.sessions_dir());
    sync.resume();
    let env = |name: &str| std::env::var_os(name);
    let (clients, warnings) =
        Clients::new(honcho, &env, Timeouts::WRITES).map_err(StartError::Secret)?;
    for warning in warnings {
        (log)(&warning);
    }
    Ok(Opened {
        sync: Arc::new(Mutex::new(sync)),
        clients,
        agent: honcho.agent.clone(),
        owner: honcho.owner.clone(),
    })
}

/// Start the worker's thread, asking `brokerd` on `config`'s state socket.
pub fn spawn(opened: Opened, config: &Config, wake: Arc<Wake>, log: Log) -> Result<(), String> {
    let socket = config.state_socket();
    let ask: AskBox =
        Box::new(move |session: &SessionId| state::ask(&socket, session, state::WAIT));
    let worker = Worker::new(
        opened.sync,
        opened.clients,
        wake,
        ask,
        log,
        Tuning::DEFAULT,
        &opened.agent,
    );
    std::thread::Builder::new()
        .name("honcho".to_string())
        .spawn(move || worker.run())
        .map(|_| ())
        .map_err(|e| format!("cannot start the Honcho worker: {e}"))
}

become

}

crates/memoryd/src/honcho/client.rs

C1. In pub struct Clients, the lines

    fleet: Client,
    agent: String,

become

    fleet: Client,
    agent: String,
    owner: String,

C2. In Clients::new, the lines

            agent: config.agent.clone(),
            ensured: HashSet::new(),

become

            agent: config.agent.clone(),
            owner: config.owner.clone(),
            ensured: HashSet::new(),

C3. The line

    /// Delete conclusion `id` in `workspace`. Gone already (404) counts as done.

becomes

    /// Make sure memory session `name` (a Boxmaker session's `<id>_memory`) exists in
    /// `workspace`, after the workspace, the agent's peer and the owner's. It holds conclusions,
    /// no messages, so neither peer observes (M5c spec, section 4).
    pub fn ensure_memory_session(
        &mut self,
        workspace: Workspace,
        name: &str,
    ) -> Result<(), HonchoError> {
        self.ensure_workspace(workspace)?;
        let (agent, owner) = (self.agent.clone(), self.owner.clone());
        self.ensure_peer(workspace, &agent)?;
        self.ensure_peer(workspace, &owner)?;
        let key = (workspace, Ensured::Session(name.to_string()));
        if self.ensured.contains(&key) {
            return Ok(());
        }
        let unobserved = || {
            SessionPeerConfig::new()
                .observe_me(false)
                .observe_others(false)
        };
        let mut peers = HashMap::new();
        peers.insert(agent, unobserved());
        peers.insert(owner, unobserved());
        let create = SessionCreate::new(name).with_peers(peers);
        created(self.client(workspace).session(&create))?;
        self.ensured.insert(key);
        Ok(())
    }

    /// Delete conclusion `id` in `workspace`. Gone already (404) counts as done.

crates/memoryd/src/honcho/mod.rs

H1. The line

pub mod decide;

becomes

pub mod decide;
pub mod edits;

H2. The line

pub mod naming;

becomes

pub mod naming;
pub mod start;

Steps

  • 1. Copy the given tests. From the repository root, three commands:

    cp docs/plans/M5c1/files/crates/memoryd/tests/honcho_edits.rs-task13 crates/memoryd/tests/honcho_edits.rs
    cp docs/plans/M5c1/files/crates/memoryd/tests/honcho_worker_more.rs-task13 crates/memoryd/tests/honcho_worker_more.rs
    cp docs/plans/M5c1/files/crates/memoryd/tests/support/honcho.rs crates/memoryd/tests/support/
    
  • 2. See them fail. With a shell timeout of 600000 ms:

    • cargo build --tests --keep-going -p memoryd: one test target does not compile, honcho_edits, with one error, E0432 (unresolved import `memoryd::honcho::edits` ). Every other target compiles.
    • cargo test -p memoryd --test honcho_worker_more --test honcho_client --test honcho_worker passes (10, 13, 12): the new fake and the retraction test agree with the code as it is.
  • 3. The new files. Make edits H1, H2 and C1 to C3, then copy:

    cp docs/plans/M5c1/files/crates/memoryd/src/honcho/edits.rs crates/memoryd/src/honcho/
    cp docs/plans/M5c1/files/crates/memoryd/src/honcho/start.rs crates/memoryd/src/honcho/
    

    cargo check -p memoryd: no error, and 22 warnings, all from the unfilled bodies: six unused import lines (bxcore::sanitize::neutralise; classify; crate::honcho::naming::HonchoSession; crate::honcho::worker::parts; ConclusionBatchCreate and ConclusionCreate; Map and json), and sixteen unused variable (session four times, clients three times, text and workspace twice each, and agent, edit, id, messages, peer once each).

  • 4. Fill the five bodies of edits.rs, one at a time, in the order of the file, each from its comment, and run cargo check -p memoryd after each: memory_session, conclude, unconclude, fleet_batch, fleet. When a body is filled, delete its instruction comment (the // lines inside the body) and keep the function's doc comment (///) as it is. After the fifth, cargo check -p memoryd prints no warning.

  • 5. The worker. Make edits W1 to W7. cargo check -p memoryd: no error and no warning. wc -l crates/memoryd/src/honcho/worker.rs prints a number under 500.

  • 6. The tests. With a shell timeout of 600000 ms, and every test passes (10, 10, 12, and the earlier ones unchanged):

    cargo test -p memoryd --test honcho_edits --test honcho_worker_more --test honcho_worker \
      --test honcho_client --test honcho_keys --test honcho_mirror_serve --test honcho_serve
    
  • 7. Check. Three commands:

    • grep -n 'fn open\|fn spawn' crates/memoryd/src/honcho/worker.rs crates/memoryd/src/honcho/start.rs prints two lines, both in start.rs.
    • grep -n 'Action::Retract' crates/memoryd/src/honcho/worker.rs prints nothing: a retraction waits.
    • 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 crates/memoryd/src/honcho/ledger.rs crates/memoryd/src/honcho/mirror.rs prints nothing.
  • 8. Run the gate. cargo fmt --all, then make gate, with a shell timeout of 600000 ms. Expected last line: gate: ok, with 1324 tests: 1314 before this task, plus 10 new ones in honcho_edits.rs.

  • 9. Log and commit. Add your row to docs/implementer-log.md; its Task cell is M5c1/13-memoryd-honcho-edits. git status --short lists honcho/mod.rs, client.rs, worker.rs, tests/support/honcho.rs, tests/honcho_worker_more.rs and your log as modified, and edits.rs, start.rs and tests/honcho_edits.rs 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 1324 tests.
  • git diff HEAD~1 --stat lists only the files of step 9.

Stop and report if

  • A "before" block of H1, H2, C1 to C3 or W1 to W7 is not in its file exactly as shown, or is there more than once.
  • Step 2 or 3 shows a different result: other test targets, errors of another kind, other warnings, or other tests failing.
  • cargo check reports an error after a body you filled or an edit you made, and the comment or the edit does not tell you how to fix it. Quote the error, and stop.
  • A comment in the skeleton does not fit the code under it, or disagrees with this task's tables. Quote both and stop.
  • After step 4 or 6 a test fails although each body follows its comment and each edit is as shown. Do not change a test, the fake or the given code: stop, and give the failing tests' names and their messages.
  • A test seems to need a real Honcho or a new fixture. It does not; stop and say why.
  • The fix seems to need a change anywhere else: serve.rs, ledger.rs, mirror.rs, 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. (gatewayd's gateway_sock test brokerd_closing_first_ends_it_as_no_longer_pending fails now and then on a loaded machine, without this task's change; if it is the one, run the gate once more.)
  • The gate passes with a count other than 1324.

What the given tests pin

Each of these changes to the finished code makes at least one given test fail (23 tried): the suffix; a conclusion not neutralised, observed by the agent, not stamped, or without its memory session made first; its id dropped; an Unconclude recording no id, or deleting something when there is nothing; a fleet note not neutralised, from the speaker, without its edit in the metadata, its session with a speaker or not made first, or over the batch limit; the memory session's owner observed, left out, or not remembered; edits left waiting; an Unconclude looked up in another workspace; a fleet note sent to the trusted workspace; a retraction sent; an edit's log label; a refused workspace ignored for edits.

After this task

  • Task 14: bxctl memory sync shows the ledger's counts and its failed items.
  • Task 15: the cutoff, turns from before Honcho was turned on skipped.