Files
boxmaker/docs/plans/M6a/01-loopd-subagent-registry.md
2026-10-03 19:39:35 -07:00

7.2 KiB

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.

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<ToolSchema>

The edits, exactly

crates/loopd/src/subagent.rs

Write this module. Do not add parse_task, dispatch_args, or run (task 02 / 03).

//! 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)

        entries.insert(
            3,
            Entry {
                schema: crate::memory::ask_memory_schema(),
                core: true,
            },
        );
        Registry::new(entries)

become

        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.

    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.