feat: live raid vars reload + MCP resource (closes #27)#70
Merged
Conversation
Watch ~/.raid/vars for the lifetime of `raid context serve` and refresh the in-memory map (via ForceLoad under the cross-process mutation lock) whenever it changes on disk, so MCP clients see fresh state when other raid processes mutate vars instead of values cached at server start. Adds `raid://workspace/vars` as a JSON MCP resource alongside the existing profile/env/repos/commands/recent set, and exposes vars on the shared Workspace snapshot. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds live reloading of persisted Raid variables for long-running raid context serve sessions and exposes those variables as a first-class MCP resource, keeping the MCP workspace view consistent with concurrent CLI invocations and manual edits.
Changes:
- Add
lib.WatchRaidVars(andraid.WatchRaidVarsre-export) to fsnotify-watch~/.raid/varswith debounce, and trigger workspace reloads under the cross-process mutation lock inraid context serve. - Extend the workspace snapshot with
Varsand add MCP resourceraid://workspace/varsthat returns an object (nevernull) even when empty. - Bump version to
0.9.0-beta, promotefsnotifyto a direct dependency, and add unit tests covering watcher behavior + snapshot copy semantics.
Reviewed changes
Copilot reviewed 13 out of 14 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/resources/app.properties | Bumps application version to 0.9.0-beta. |
| src/raid/raid.go | Re-exports WatchRaidVars from internal/lib at the public API layer. |
| src/raid/raid_test.go | Adds coverage ensuring the public raid.WatchRaidVars rejects nil callbacks. |
| src/internal/lib/lib.go | Implements vars snapshot copying and the fsnotify-based vars watcher with debouncing. |
| src/internal/lib/vars_watch_test.go | Adds tests for watcher behavior (debounce/coalesce/cancel/error paths) and snapshot semantics. |
| src/internal/lib/context.go | Adds Workspace.Vars and includes a defensive copy in GetWorkspaceContext. |
| src/internal/lib/context_test.go | Tests that vars are included and are copy-safe / omitted when empty. |
| src/cmd/context/serve.go | Starts vars watcher for MCP server and adds raid://workspace/vars resource handler. |
| src/cmd/context/serve_test.go | Adds tests for vars resource shape and watcher-start wiring. |
| site/docs/whats-new.mdx | Documents upcoming 0.9.0 features related to live vars + MCP resource. |
| site/docs/usage/context.mdx | Updates MCP resource list to include raid://workspace/vars and describes live reload behavior. |
| llms.txt | Updates high-level project index to mention context serve + live vars resource. |
| go.mod | Promotes fsnotify (and flock) to direct dependencies. |
| go.sum | Updates sums consistent with dependency graph changes. |
Comment on lines
+130
to
+132
| _ = raid.WithMutationLock(func() error { | ||
| return raid.ForceLoad() | ||
| }) |
Comment on lines
+175
to
+181
| var timer *time.Timer | ||
| for { | ||
| select { | ||
| case <-ctx.Done(): | ||
| if timer != nil { | ||
| timer.Stop() | ||
| } |
…rs-watcher # Conflicts: # go.mod
Copilot review feedback on #70: - startVarsWatcher now logs ForceLoad failures to stderr instead of swallowing them, so a recurring reload failure (bad profile after an external edit, transient IO) is visible rather than silently leaving the MCP server with a stale snapshot. - runVarsWatcher switches from time.AfterFunc to a timer.C channel consumed inside the same select that watches ctx.Done. This makes onChange run from the watcher goroutine — gated by the same lifecycle signal — instead of a separate timer goroutine that could fire after ctx was already cancelled. A belt-and-braces ctx.Err() check before the reload covers the same-tick race where both channels become ready simultaneously. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #70 +/- ##
==========================================
- Coverage 91.56% 90.72% -0.84%
==========================================
Files 33 33
Lines 2678 2772 +94
==========================================
+ Hits 2452 2515 +63
- Misses 147 168 +21
- Partials 79 89 +10 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Resolves #27 — Add watcher on variables that executes updates live.
raid context servenow watches~/.raid/vars(via fsnotify on the parent dir, debounced 50ms to coalesce atomic-rename bursts) and reloads the in-memory workspace whenever the file changes. Reload runsForceLoadunder the cross-process mutation lock so it serializes cleanly with concurrentraid_install/raid_env_switch/raid_run_taskmutations.raid://workspace/vars(JSON) exposes the persisted variables —Set-task values plus the auto-derivedRAID_REPO_*keys — alongside the existingprofile,env,repos,commands,recentset. The handler returns{}rather thannullfor the empty case so clients don't need to special-case it.Workspacesnapshot gains aVarsfield;GetWorkspaceContextpopulates it via a defensive copy underraidVarsMu.RLock()so callers cannot mutate the live map.fsnotifypromoted from indirect to direct ingo.mod.Why
raidVarswas loaded once at process start. The MCP server is long-running, so when another raid process (or a hand edit) wrote to~/.raid/vars, the server kept serving stale state and AI agents saw lies. Variables also weren't exposed as a resource at all — issue #27 was driven by both gaps.Test plan
go test ./...— all packages green.raid.WatchRaidVarsre-export.go buildsucceeds;raid --versionreports0.9.0-beta.cd site && npm run build— no broken links.raid context serve, write to~/.raid/varsfrom another shell, confirmraid://workspace/varsreflects the change in <100ms.Out of scope
WithResourceCapabilities(false, false); subscription support is a separate, larger change.profile.raid.ymlor per-reporaid.yamlfor live reload. Same mechanism would compose, but Add watcher on variables that executes updates live #27 is scoped to vars.🤖 Generated with Claude Code