# M6a task 01: `subagent` in the M5c registry, not core **Branch:** `m6a`. Check it: `git branch --show-current` prints `m6a` and `git status --short` is empty; otherwise stop. Never switch branch. **Commit subject:** `loopd: subagent in the M5c registry, not core` **Log row:** the Task cell is `M6a/01-loopd-subagent-registry`. **First action.** Run this before any Read of crate source. Do not `ls` `crates/loopd/tests/`. Do not Read `tests/support/`, `turn.rs`, `session.rs`, `docs/implementer-log.md` (except the first 12 lines at the log step). The signatures below are complete. ```sh cp docs/plans/M6a/files/crates/loopd/tests/subagent_registry.rs crates/loopd/tests/ ``` ## Goal `subagent` is in `Registry::m5c()`, not core. `find_tool` finds it. A new session's epoch `tools` array stays `clock`, `recall`, `memory`, `ask_memory`, `find_tool`, `call_tool` (spec 3.2). `loopd` does not answer the call yet (task 02). ## Every place that changes | Place | What happens there | |---|---| | `crates/loopd/src/subagent.rs` | new: `SUBAGENT`, `schema()` | | `crates/loopd/src/lib.rs` | `pub mod subagent;` | | `crates/loopd/src/tools.rs` | `m5c()` pushes the non-core entry | | `crates/loopd/tests/subagent_registry.rs` | new, given | | everything else | **no change**: not `dispatch`, not `turn.rs` | ## Files - Copy, from `docs/plans/M6a/files/` (the same path under it): the new test `crates/loopd/tests/subagent_registry.rs`. - Create: `crates/loopd/src/subagent.rs` (the module below). - Modify: `crates/loopd/src/lib.rs`, `crates/loopd/src/tools.rs`, `docs/implementer-log.md`. - Do not touch: any other file. At the log-and-commit step, read only the first 12 lines of `docs/implementer-log.md`. ## Facts you need - **Not core.** `core_schemas()` already filters `e.core`. A non-core `Entry` is found by `find` and by `get`, and is absent from `Baseline.tools`. - **`m5c()` only.** Do not add the entry to `m2b`, `m3a`, or `m5a`. `loopd serve` uses `m5c`. - **`push`, not `insert`.** `ask_memory` stays at index 3. `subagent` is last among the non-core names, after `http_fetch`. - rustfmt `mod` order: `subagent` after `sse` / before `summarise` as rustfmt decides. - Same-crate modules may import each other. This task's module imports nothing from `turn` or `tools`. - The items the edits use. This table is complete. | Item | Signature | |---|---| | `ToolSchema` | `{ name: String, description: String, parameters: serde_json::Value }` in `crate::llama` | | `Entry` | `{ schema: ToolSchema, core: bool }` | | `Registry::m5c` | `m5c() -> Registry` | | `Registry::get` | `get(&self, name: &str) -> Option<&Entry>` | | `Registry::find` | `find(&self, query: &str) -> Vec<&ToolSchema>` | | `Registry::core_schemas` | `core_schemas(&self) -> Vec` | ## The edits, exactly ### `crates/loopd/src/subagent.rs` Write this module. Do not add `parse_task`, `dispatch_args`, or `run` (task 02 / 03). ```rust //! The `subagent` tool: loopd answers it itself (M6 spec, section 3). //! It is not core; the parent finds it with `find_tool` and calls it through `call_tool`. pub const SUBAGENT: &str = "subagent"; pub fn schema() -> crate::llama::ToolSchema { crate::llama::ToolSchema { name: SUBAGENT.to_string(), description: "Run a short task in an isolated child session on the background slot." .to_string(), parameters: serde_json::json!({ "type": "object", "properties": { "task": { "type": "string", "description": "What the child should do." } }, "required": ["task"] }), } } ``` ### `crates/loopd/src/lib.rs` **L1.** After `pub mod sse;` add `pub mod subagent;`. rustfmt decides the final order. ### `crates/loopd/src/tools.rs` **R1.** The lines (exactly once, inside `m5c`) ```rust entries.insert( 3, Entry { schema: crate::memory::ask_memory_schema(), core: true, }, ); Registry::new(entries) ``` become ```rust entries.insert( 3, Entry { schema: crate::memory::ask_memory_schema(), core: true, }, ); entries.push(Entry { schema: crate::subagent::schema(), core: false, }); Registry::new(entries) ``` ## Steps - [ ] **1. Copy the given test.** ```sh cp docs/plans/M6a/files/crates/loopd/tests/subagent_registry.rs crates/loopd/tests/ ``` - [ ] **2. See it fail.** `cargo build --tests --keep-going -p loopd`, with a shell timeout of 600000 ms: test target `subagent_registry` does not compile (`E0432` unresolved `loopd::subagent`). Every other target compiles. - [ ] **3. The edits.** Write `subagent.rs`, L1, R1. `cargo check -p loopd` prints no error and no warning. `wc -l crates/loopd/src/subagent.rs` and `wc -l crates/loopd/src/tools.rs` each ≤ 500. - [ ] **4. The tests.** `cargo test -p loopd --test subagent_registry --test ask_memory_tool --test tools --test baseline`: every test passes, 2 in `subagent_registry`. - [ ] **5. Check.** `git diff --stat` lists exactly `crates/loopd/src/subagent.rs`, `crates/loopd/src/lib.rs`, `crates/loopd/src/tools.rs`. `git status --short` also shows the new test as untracked. - [ ] **6. Run the gate.** `cargo fmt --all`, then `make gate`, with a shell timeout of 600000 ms. Expected last line: `gate: ok`. That is done counting: **1577** (1575 before, plus the 2 of `subagent_registry.rs`). After `gate: ok`, do not add up `test result:` lines and do not run `cargo test --workspace`. - [ ] **7. Log and commit.** Add your row to `docs/implementer-log.md`; its Task cell is `M6a/01-loopd-subagent-registry`. Read only the first 12 lines of that file; insert the row under the header. Stage with `git add crates/loopd 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 1577 tests. - `git diff HEAD~1 --stat` lists only `crates/loopd/src/subagent.rs`, `crates/loopd/src/lib.rs`, `crates/loopd/src/tools.rs`, `crates/loopd/tests/subagent_registry.rs` and `docs/implementer-log.md`. ## Stop and report if - The branch check at the top fails. - A "before" block is not in its file exactly as shown, or is there more than once. - Step 2 shows a different result: the tests compile, or a different crate fails. - `cargo check` in step 3 prints an error or a warning. Quote it, and stop. - A test fails in step 4. Do not change the test: stop, and give the failing tests' names and their messages. - The fix seems to need a change anywhere else. It does not; stop and say why you think so. - A test outside `subagent_registry.rs` fails in the gate: check once whether it fails without your change (`git stash -u`, run it, `git stash pop`), then stop and report both results. ## What the given tests pin `subagent` is in `m5c()`, `core` is false, `schema()` matches the entry, `find("subagent")` and `find("isolated")` both hit it, and a baseline assembled from `m5c()` still lists only the six core names in `tools`.