9.0 KiB
Phase 1 Step 8d — Implementation Notes
Status
Done.
Plan reference
- Plan:
docs/PHASE-1-IMPLEMENTATION-PLAN.md - Section: "Step 8d: Scope transition rewrites —
use_role,use_session,use_agent,exit_agent"
Summary
Added scope transition methods to RequestContext that build real
ToolScope instances via McpFactory::acquire(). Added
mcp_config and mcp_log_path fields to AppState so scope
transitions can look up MCP server specs and acquire handles. Added
Session::new_from_ctx and Session::load_from_ctx constructors
that take &RequestContext + &AppConfig instead of &Config.
Migrated edit_role (deferred from Step 8b) since use_role is
now available. use_agent is deferred to Step 8h because
Agent::init takes &GlobalConfig.
What was changed
Files modified (4 files)
-
src/config/app_state.rs— added 2 fields:mcp_config: Option<McpServersConfig>— parsed MCP server specs frommcp.json, stored at init time for scope transitions to look up server specs by namemcp_log_path: Option<PathBuf>— log path for MCP server stderr output, passed toMcpFactory::acquire
-
src/config/request_context.rs— added 6 methods in a new impl block:rebuild_tool_scope(&mut self, app, enabled_mcp_servers)— private async helper that resolves MCP server IDs, acquires handles viaMcpFactory::acquire(), builds a freshFunctionsinstance, appends user interaction and MCP meta functions, assembles aToolScope, and assigns it toself.tool_scopeuse_role(&mut self, app, name, abort_signal)— retrieves the role, resolves its MCP server list, callsrebuild_tool_scope, thenuse_role_objuse_session(&mut self, app, session_name, abort_signal)— creates or loads a session viaSession::new_from_ctx/Session::load_from_ctx, rebuilds the tool scope, handles the "carry last message" prompt, callsinit_agent_session_variablesexit_agent(&mut self, app)— exits the session, resets the tool scope to a fresh default (global functions + user interaction), clears agent/supervisor/rag stateedit_role(&mut self, app, abort_signal)— resolves the current role name, callsupsert_role(editor), thenuse_roleupsert_role(&self, app, name)— opens the role file in the editor (viaapp.editor())
Updated imports:
McpRuntime,TEMP_SESSION_NAME,AbortSignal,formatdoc,Confirm,remove_file. -
src/config/session.rs— added 2 constructors:Session::new_from_ctx(&RequestContext, &AppConfig, name)— equivalent toSession::new(&Config, name)but readsctx.extract_role(app)andapp.save_sessionSession::load_from_ctx(&RequestContext, &AppConfig, name, path)— equivalent toSession::load(&Config, name, path)but callsModel::retrieve_model(app, ...)andctx.retrieve_role(app, role_name)instead of&Configmethods
-
src/config/bridge.rs— addedmcp_config: None, mcp_log_path: Noneto all 3AppStateconstruction sites in tests
Files NOT changed
src/mcp/mod.rs— untouched; Step 8c's extraction is used viaMcpFactory::acquire()src/config/mcp_factory.rs— untouchedsrc/config/mod.rs— allConfig::use_role,Config::use_session,Config::use_agent,Config::exit_agentstay intact for current callers
Key decisions
1. rebuild_tool_scope replaces McpRegistry::reinit
The existing Config::use_role and Config::use_session both
follow the pattern: take McpRegistry → McpRegistry::reinit →
put registry back. The new rebuild_tool_scope replaces this with:
resolve server IDs → McpFactory::acquire() each → build
ToolScope. This is the core semantic change from the plan.
Key differences:
McpRegistry::reinitdoes batch start/stop of servers (stops servers not in the new set, starts missing ones). The factory approach acquires each server independently — unused servers are dropped when theirArcrefcount hits zero.- The factory's
Weaksharing means that switching from role A (github,slack) to role B (github,jira) shares the github handle instead of stopping and restarting it.
2. ToolCallTracker initialized with default params
ToolCallTracker::new(4, 10) — 4 max repeats, 10 chain length.
These match the constants used in the existing codebase (the
tracker is used for tool-call loop detection). A future step can
make these configurable via AppConfig if needed.
3. use_agent deferred to Step 8h
Config::use_agent is a static method that takes &GlobalConfig
and calls Agent::init(config, agent_name, abort_signal).
Agent::init compiles agent tools, loads RAG, resolves the model,
and does ~100 lines of setup, all against &Config. Migrating
Agent::init is a significant cross-module change that belongs
in Step 8h alongside the other agent lifecycle methods.
The plan listed use_agent as a target for 8d, but the
dependency on Agent::init(&Config) makes a clean bridge
impossible without duplicating Agent::init.
4. abort_signal is unused in the new methods
The existing Config::use_role doesn't pass abort_signal to
individual server starts — it's used by abortable_run_with_spinner
wrapping the batch McpRegistry::reinit. The new methods use
McpFactory::acquire() which doesn't take an abort signal (see
Step 8c notes). The _abort_signal parameter is kept in the
signature for API compatibility; Step 8f can wire it into the
factory if per-server cancellation is needed.
5. Session constructors parallel existing ones
Session::new_from_ctx and Session::load_from_ctx are verbatim
copies of Session::new and Session::load with config: &Config
replaced by ctx: &RequestContext + app: &AppConfig. The copies
are under #[allow(dead_code)] and will replace the originals
when callers migrate in Steps 8f-8g.
6. exit_agent rebuilds tool scope inline
Config::exit_agent calls self.load_functions() to reset the
global function declarations after exiting an agent. The new
exit_agent does the equivalent inline: creates a fresh
ToolScope with Functions::init() + user interaction functions.
It does NOT call rebuild_tool_scope because there's no MCP
server set to resolve — we're returning to the global scope.
Deviations from plan
| Deviation | Rationale |
|---|---|
use_agent deferred to Step 8h |
Depends on Agent::init(&Config) migration |
No abort_signal propagation to McpFactory::acquire |
Step 8c decided against it; behavior matches existing code |
| No parent scope restoration test | Testing requires spawning real MCP servers; documented as Phase 5 test target |
Verification
Compilation
cargo check— clean, zero warnings, zero errorscargo clippy— clean
Tests
cargo test— 63 passed, 0 failed (unchanged)
Handoff to next step
What Step 8e can rely on
RequestContext::use_role(app, name, abort_signal)— full scope transition with ToolScope rebuild via McpFactoryRequestContext::use_session(app, session_name, abort_signal)— full scope transition with Session creation/loadingRequestContext::exit_agent(app)— cleans up agent state and rebuilds global ToolScopeRequestContext::edit_role(app, abort_signal)— editor + use_roleRequestContext::upsert_role(app, name)— editor onlySession::new_from_ctx/Session::load_from_ctx— ctx- compatible session constructorsAppState.mcp_config/AppState.mcp_log_path— MCP server specs and log path available for scope transitions
Method count at end of Step 8d
AppConfig: 21 methods (unchanged from 8b)RequestContext: 53 methods (46 from 8b + 6 from 8d + 1 privaterebuild_tool_scope)Session: 2 new constructors (new_from_ctx,load_from_ctx)AppState: 2 new fields (mcp_config,mcp_log_path)
What Step 8e should do
Migrate the Category C deferrals from Step 6:
compress_session,maybe_compress_sessionautoname_session,maybe_autoname_sessionuse_rag,edit_rag_docs,rebuild_ragapply_prelude
Files to re-read at the start of Step 8e
docs/PHASE-1-IMPLEMENTATION-PLAN.md— Step 8e section- This notes file
- Step 6 notes — Category C deferral inventory
src/config/rag_cache.rs— RagCache scaffolding from Step 6.5src/config/mod.rs—compress_session,maybe_compress_session,autoname_session,maybe_autoname_session,use_rag,edit_rag_docs,rebuild_rag,apply_preludemethod bodies
References
- Phase 1 plan:
docs/PHASE-1-IMPLEMENTATION-PLAN.md - Step 8b notes:
docs/implementation/PHASE-1-STEP-8b-NOTES.md - Step 8c notes:
docs/implementation/PHASE-1-STEP-8c-NOTES.md - Step 6.5 notes:
docs/implementation/PHASE-1-STEP-6.5-NOTES.md - Modified files:
src/config/request_context.rs(6 new methods)src/config/app_state.rs(2 new fields)src/config/session.rs(2 new constructors)src/config/bridge.rs(test updates for new AppState fields)