Skip to content

feat(context): config hot-reload via ContextPipeline middleware#111

Merged
yishuiliunian merged 5 commits intomainfrom
feat/config-hot-reload
Apr 15, 2026
Merged

feat(context): config hot-reload via ContextPipeline middleware#111
yishuiliunian merged 5 commits intomainfrom
feat/config-hot-reload

Conversation

@yishuiliunian
Copy link
Copy Markdown
Contributor

Summary

  • Wire ContextPipeline (previously test-only) into the agent loop as a pre-LLM middleware pipeline
  • Add ConfigRefreshMiddleware that detects mid-session file changes (memory, instructions, settings) via mtime polling and injects diffs as system-reminder user messages
  • System prompt is never modified — Anthropic prefix cache is preserved

Changes

New files (loopal-context):

  • middleware/file_snapshot.rsFileSnapshot with stat-then-read TOCTOU safety, ordered line diff preserving duplicates, formatted change summaries
  • middleware/config_refresh.rsConfigRefreshMiddleware impl with Mutex<Vec<FileSnapshot>> for interior mutability
  • tests/suite/file_snapshot_test.rs — 15 tests (diff, format, snapshot lifecycle, unicode, sequential changes)
  • tests/suite/config_refresh_test.rs — 5 tests (no-change, injection, dedup, cache safety, multi-file)

New files (loopal-runtime):

  • agent_loop/context_pipeline.rs — bridge method run_context_pipeline() (non-fatal, errors logged and swallowed)
  • agent_loop/pipeline_setup.rsbuild_context_pipeline() watching 9 file paths (global+project memory/instructions/settings)
  • agent_loop/params.rs — extracted AgentConfig, AgentLoopParams, AgentDeps, etc. from mod.rs
  • agent_loop/turn_tool_phase.rs — extracted tool execution phase from turn_exec.rs

Modified files:

  • middleware/mod.rs — export new modules
  • runner.rs — added pipeline: ContextPipeline field
  • turn_exec.rs — call run_context_pipeline() before LLM, delegate tool phase
  • mod.rs — build pipeline in agent_loop(), re-export from params.rs

Watched paths (9 total)

File Label
~/.loopal/memory/MEMORY.md Global Memory
.loopal/memory/MEMORY.md Project Memory
~/.loopal/LOOPAL.md Global Instructions
~/.loopal/LOOPAL.local.md Global Local Instructions
LOOPAL.md Project Instructions
.loopal/LOOPAL.local.md Local Instructions
~/.loopal/settings.json Global Settings
.loopal/settings.json Project Settings
.loopal/settings.local.json Local Settings

Test plan

  • bazel build //... --config=clippy — zero warnings
  • bazel test //crates/loopal-context:loopal-context_test — 20 tests pass (including new)
  • bazel test //crates/loopal-runtime:loopal-runtime_test — all pass
  • CI passes

Wire the existing ContextPipeline into the agent loop so middleware
runs before every LLM call. Add ConfigRefreshMiddleware that detects
mid-session changes to memory, instructions, and settings files via
mtime polling, computes line-level diffs, and injects system-reminder
messages into the working message copy — without touching the system
prompt (preserving Anthropic prefix cache).

Key changes:
- file_snapshot: FileSnapshot with stat-then-read ordering, ordered
  line diff preserving duplicates, formatted change summaries
- config_refresh: Middleware checking 9 watched paths (global+project
  memory, instructions, settings) with Mutex for interior mutability
- context_pipeline: bridge method on AgentLoopRunner (non-fatal)
- pipeline_setup: builds pipeline with all watched paths
- params: extracted types from mod.rs to stay under 200-line limit
- turn_tool_phase: extracted tool execution phase from turn_exec.rs
Windows filesystems can have coarse mtime granularity (up to 1s on
HFS+/NTFS). Tests that write a file and immediately check for changes
may see the same mtime. Add 1.1s sleep between snapshot load and
file modification in affected tests.
@yishuiliunian yishuiliunian merged commit c16c02e into main Apr 15, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant