# M5a task 12: decided memory calls go to `memoryd`, and labels only get stricter **Branch:** `m5a` (run `git switch m5a`; `git status --short` must be empty, otherwise stop) **Commit subject:** `brokerd: memory calls to memoryd with the session's state; labels only stricter` ## Goal M5a spec, section 8: - A decided `recall` or `memory` call goes to `memoryd`'s `brokerd` socket as a `memory_call`, with the session's taint and untrusted flag **as `brokerd` held them when it decided**. Every other call goes to the configured runtime, as before (`Routed`). - `memoryd` knows the label of what it returns (a `recall` hit from an untrusted session). The result keeps the **stricter** of that and the grant's label: the higher class, and untrusted if either says so. A runtime can never make a result laxer. - The container runtime refuses a memory tool rather than start a container for it. - `memoryd` not answering in 30 seconds is a failed result with a fixed sentence and a runbook pointer. To carry the session's state, `Decision` gains a private field (`decide` and `redecide` already receive the state), and `RunSpec` gains one too. `Runtime` gains `run_labelled`, with a default that calls `run` and returns no label, so no existing runtime changes. ## Files - Copy (skeleton): `crates/brokerd/src/memory_port.rs` - Copy (tests): `crates/brokerd/tests/memory_port.rs`, and a **new version** of the given `crates/brokerd/tests/support/rig.rs` (its `Config` literal gains `memory`) - Append: `docs/plans/M5a/files/runbook-memoryd-unreachable.md` to the end of `docs/runbook.md` (a blank line before it) - Modify: `crates/brokerd/src/lib.rs` (`pub mod memory_port;` after `pub mod ledger;`), `crates/brokerd/src/policy/mod.rs`, `crates/brokerd/src/runner.rs`, `crates/brokerd/src/container.rs`, `crates/brokerd/src/config.rs`, `crates/brokerd/src/main.rs`, `docs/implementer-log.md` ## The skeleton Written: `UNREACHABLE`, `MEMORY_WAIT`, `MemoryPort` and `new`, `Routed`, and both `run` methods (they call `run_labelled`). Fill: `MemoryPort::ask`, `MemoryPort::run_labelled`, `Routed::run_labelled`. ## `policy/mod.rs`, exactly 1. In `struct Decision`, after `matched: Matched,` add: ```rust /// The session's state when the call was decided; `memoryd` needs it (M5a spec, section 8). state: SessionState, ``` (Only `Decision`: `Ask` is unchanged.) 2. In `impl Decision`, after `request()` add: ```rust pub fn state(&self) -> SessionState { self.state } ``` 3. In `decide`, the `Mode::Auto => Outcome::Allowed(Decision { request, args, matched, })` literal gains `state,` after `matched,`; in `redecide`, the `Ok(Decision { … matched, })` literal gains `state,` after `matched,`. Both functions already have `state` as a parameter. ## `runner.rs`, exactly 1. `use crate::policy::Decision;` becomes `use crate::policy::{Decision, Label, SessionState};`. 2. In `struct RunSpec`, after `egress: Option>,` add `state: SessionState,`; in `impl RunSpec`, after `egress()` add: ```rust /// The session's state when the call was decided. pub fn state(&self) -> SessionState { self.state } ``` 3. In `trait Runtime`, after `fn run(…);` add: ```rust /// `run`, and a label for what came back when the runtime knows one (`memoryd` does: a /// `recall` hit from an untrusted session). The runner keeps the stricter of this and the /// grant's label. Container runtimes know none. fn run_labelled(&self, spec: &RunSpec) -> Result<(RunOutput, Option