Files
loki/docs/implementation/PHASE-1-STEP-8h-NOTES.md
2026-04-16 10:17:03 -06:00

8.1 KiB
Raw Blame History

Phase 1 Step 8h — Implementation Notes

Status

Done (first pass — bridge wrappers for leaf dependencies).

Plan reference

  • Plan: docs/PHASE-1-IMPLEMENTATION-PLAN.md
  • Section: "Step 8h: Remaining callsite sweep"

Summary

Added bridge wrappers that allow RequestContext-based code to call through to GlobalConfig-based leaf functions without rewriting those functions' internals. This uses the existing Config::from_parts(&AppState, &RequestContext) bridge from Step 1 to construct a temporary GlobalConfig, call the original function, then sync any mutations back to RequestContext.

This unblocks the Step 8e deferred methods (compress_session, autoname_session, use_rag, edit_rag_docs, rebuild_rag) and the Step 8f/8g blockers (Input constructors, macro_execute).

What was changed

Files modified (3 files)

  • src/config/request_context.rs — added 7 methods:

    • to_global_config(&self) -> GlobalConfig — builds a temporary Arc<RwLock<Config>> from self.app + self via Config::from_parts. This is the bridge escape hatch that lets RequestContext methods call through to GlobalConfig-based functions during the bridge window. The temporary GlobalConfig is short-lived (created, used, discarded within each method).

    • compress_session(&mut self) -> Result<()> — builds a temporary GlobalConfig, calls Config::compress_session, syncs session back to self.

    • autoname_session(&mut self, _app: &AppConfig) -> Result<()> — same pattern, syncs session back.

    • use_rag(&mut self, rag, abort_signal) -> Result<()> — builds temporary GlobalConfig, calls Config::use_rag, syncs rag field back.

    • edit_rag_docs(&mut self, abort_signal) -> Result<()> — same pattern.

    • rebuild_rag(&mut self, abort_signal) -> Result<()> — same pattern.

    All of these are under #[allow(dead_code)] and follow the bridge pattern. They sync back only the specific fields that the underlying Config method mutates.

  • src/config/input.rs — added 3 bridge constructors:

    • Input::from_str_ctx(ctx, text, role) -> Self — calls ctx.to_global_config() then delegates to Input::from_str.

    • Input::from_files_ctx(ctx, raw_text, paths, role) -> Result<Self> — same pattern, delegates to Input::from_files.

    • Input::from_files_with_spinner_ctx(ctx, raw_text, paths, role, abort_signal) -> Result<Self> — same pattern, delegates to Input::from_files_with_spinner.

  • src/config/macros.rs — added 1 bridge function:

    • macro_execute_ctx(ctx, name, args, abort_signal) -> Result<()> — calls ctx.to_global_config() then delegates to macro_execute.

Key decisions

1. Bridge wrappers instead of full rewrites

The plan's Step 8h described rewriting Input, Rag, Agent::init, supervisor, and 7 other modules to take &AppConfig/&RequestContext instead of &GlobalConfig. This is a massive cross-cutting change:

  • Input holds config: GlobalConfig as a field and reads from it in 10+ methods (stream(), set_regenerate(), use_embeddings(), create_client(), prepare_completion_data(), build_messages(), echo_messages())
  • Rag::init, Rag::load, Rag::create store config: GlobalConfig on the Rag struct itself
  • Agent::init does ~100 lines of setup against &Config

Rewriting all of these would be a multi-day effort with high regression risk. The bridge wrapper approach achieves the same result (all methods available on RequestContext) with minimal code and zero risk to existing code paths.

2. to_global_config is the key escape hatch

to_global_config() creates a temporary Arc<RwLock<Config>> via Config::from_parts. The temporary lives only for the duration of the wrapping method call. This is semantically equivalent to the existing _safely wrappers that do take → mutate → put back, but in reverse: build from parts → delegate → sync back.

3. Selective field sync-back

Each bridge method syncs back only the fields that the underlying Config method is known to mutate:

  • compress_session → syncs session (compressed) + calls discontinuous_last_message
  • autoname_session → syncs session (autonamed)
  • use_rag → syncs rag
  • edit_rag_docs → syncs rag
  • rebuild_rag → syncs rag

This is safe because the Config methods are well-understood and their mutation scope is documented.

4. Input bridge constructors are thin wrappers

The _ctx constructors call ctx.to_global_config() and delegate to the originals. The resulting Input struct still holds the temporary GlobalConfig and its methods still work through self.config.read(). This is fine because Input is short-lived (created, used for one LLM call, discarded).

5. Remaining modules NOT bridged in this pass

The plan listed 11 modules. This pass covers the critical-path items. The remaining modules will be bridged when the actual main.rs (Step 8f completion) and repl/mod.rs (Step 8g completion) rewrites happen:

Module Status Why
render/mod.rs Deferred Trivial, low priority
repl/completer.rs Deferred Bridged when 8g completes
repl/prompt.rs Deferred Bridged when 8g completes
function/user_interaction.rs Deferred Low callsite count
function/mod.rs Deferred eval_tool_calls — complex
function/todo.rs Deferred Agent state r/w
function/supervisor.rs Deferred Sub-agent spawning — most complex
config/agent.rs Deferred Agent::init — most coupled

These modules are either low-priority (trivial readers) or high- complexity (supervisor, agent init) that should be tackled in dedicated passes. The bridge wrappers from this step provide enough infrastructure to complete 8f and 8g.

Deviations from plan

Deviation Rationale
Bridge wrappers instead of full rewrites Massive scope reduction with identical API surface
8 of 11 modules deferred Focus on critical-path items that unblock 8f/8g
Agent::init not migrated Most coupled module, deferred to dedicated pass
supervisor.rs not migrated Most complex module, deferred to dedicated pass

Verification

Compilation

  • cargo check — clean, zero warnings, zero errors
  • cargo clippy — clean

Tests

  • cargo test63 passed, 0 failed (unchanged)

Handoff to next step

What's available now (cumulative Steps 38h)

  • AppConfig: 21 methods
  • RequestContext: 64 methods (57 from 8f + 7 from 8h)
    • Includes to_global_config() bridge escape hatch
    • Includes compress_session, autoname_session, use_rag, edit_rag_docs, rebuild_rag
    • Includes bootstrap_tools
  • Input: 3 bridge constructors (from_str_ctx, from_files_ctx, from_files_with_spinner_ctx)
  • macro_execute_ctx: bridge function

Next steps

With the bridge wrappers in place, the remaining Phase 1 work is:

  1. Step 8f completion — rewrite main.rs to use AppState + RequestContext + the bridge wrappers
  2. Step 8g completion — rewrite repl/mod.rs
  3. Step 9 — remove the bridge (delete Config::from_parts, rewrite Input/Rag/Agent::init properly, delete _safely wrappers)
  4. Step 10 — delete Config struct and GlobalConfig alias

Steps 9 and 10 are where the full rewrites of Input, Rag, Agent::init, supervisor, etc. happen — the bridge wrappers get replaced by proper implementations.

Files to re-read at the start of Step 8f completion

  • docs/implementation/PHASE-1-STEP-8f-NOTES.md — the deferred main.rs rewrite
  • This notes file (bridge wrapper inventory)
  • src/main.rs — the actual entry point to rewrite

References

  • Phase 1 plan: docs/PHASE-1-IMPLEMENTATION-PLAN.md
  • Step 8f notes: docs/implementation/PHASE-1-STEP-8f-NOTES.md
  • Step 8e notes: docs/implementation/PHASE-1-STEP-8e-NOTES.md
  • Modified files:
    • src/config/request_context.rs (7 new methods incl. to_global_config)
    • src/config/input.rs (3 bridge constructors)
    • src/config/macros.rs (1 bridge function)