File: C:\Code\Repos\SageFs\SageFs\McpTools.fs (line ~1190) Definition: member _.enable_live_testing() : Task
File: C:\Code\Repos\SageFs\SageFs.Core\Mcp.fs (line 1643) Implementation: let setLiveTesting (ctx: McpContext) (enabled: bool) : Task
Flow:
- MCP tool calls enable_live_testing()
- Calls setLiveTesting(ctx, true)
- Dispatches SageFsMsg.EnableLiveTesting to Elm
- Returns async response: "Live testing enabled. Discovery runs asynchronously..."
- Notes cold-session priming requirement
File: C:\Code\Repos\SageFs\SageFs.Core\SageFsApp.fs (line 798) Handler: | SageFsMsg.EnableLiveTesting ->
Update behavior:
- Sets LiveTestingActivation.Active
- If DiscoveredTests is NOT empty: triggers execution for those tests
- If DiscoveredTests IS empty: returns no effects (no-op)
- Returns recomputed model
Current Behavior:
- WARM: Worker calls WorkerMessage.GetTestDiscovery on startup → SessionManager handles InitialTestDiscovery → populates DiscoveredTests
- COLD (enable_live_testing on empty session):
- Sets Activation=Active
- DiscoveredTests is empty → no effects generated
- NO discovery is triggered
- Client must call eval (any expression) to trigger file change detection
- Only keystroke/file-change → triggers FCS → triggers discovery indirectly
Root Cause: Discovery only runs when:
- Worker sends it initially (warm startup)
- File changes trigger FCS type check → afterTypeCheck may run tests
- EnableLiveTesting never directly triggers discovery on cold session
File: C:\Code\Repos\SageFs\SageFs.Core\Features\LiveTestingTypes.fs (line 1133) Type: ype LiveTestDiscoveryState = Disabled | Discovering | ReadyZeroTests | ReadyWithTests of count
File: C:\Code\Repos\SageFs\SageFs.Core\Features\LiveTestingTypes.fs (line 1157) Key Function: LiveTestState.requiresPrimingEval : LiveTestState -> bool
Implementation (line 1157-1162):
sharp let requiresPrimingEval (state: LiveTestState) = match state.Activation with | LiveTestingActivation.Inactive -> false | LiveTestingActivation.Active -> Array.isEmpty state.DiscoveredTests && state.LastDiscoveryTime <= System.DateTimeOffset.MinValue
Returns TRUE when:
- Activation is Active AND
- No tests discovered yet AND
- Discovery has never run (LastDiscoveryTime = MinValue)
Wire Value (line 1140-1144):
- Disabled → "disabled"
- Discovering → "discovering"
- ReadyZeroTests → "ready_zero_tests"
- ReadyWithTests n → "ready_with_tests"
File: C:\Code\Repos\SageFs\SageFs.Tests\LiveTestingDiscoveryStateTests.fs
Core tests:
- Line 40: "inactive state is disabled"
- Line 46: "active state with no discovery timestamp is discovering"
- Line 53: "active state with zero tests after discovery is ready_zero_tests"
- Line 64: "active state with discovered tests is ready_with_tests"
- Line 76: "wire values are stable"
- Line 89: "discovering hint explains cold-session priming"
MCP Discovery Semantics (line 97): 7. Line 99: "enable_live_testing explains async cold-start discovery" 8. Line 108: "get_live_test_status exposes priming requirement while discovery is pending" 9. Line 124: "get_test_trace exposes priming requirement while discovery is pending"
Tests verify:
- DiscoveryState enum transitions
- Wire value stability
- MCP hints reference "no-op eval"
- DiscoveryRequiresEval flag surfaces in get_live_test_status / get_test_trace
Option A: Trigger discovery on enable_live_testing (cold start)
- Location: SageFsApp.fs line 798-821 (EnableLiveTesting handler)
- Change: When Activation transitions to Active AND DiscoveredTests is empty, generate a discovery effect
- Challenge: Discovery is a worker RPC (GetTestDiscovery), requires session ID routing
Option B: Auto-prime discovery via keystroke simulator
- Location: SageFsApp.fs or Elm loop
- Change: When EnableLiveTesting fires with empty DiscoveredTests, dispatch a synthetic FileContentChanged
- Advantage: Reuses existing hot-reload → FCS → discovery chain
- Risk: May trigger incomplete discovery if syntax is bad
Option C: Explicit no-op eval flow (current smoke-test approach)
- Location: Client-side (MCP caller)
- Current: Smoke test sends "1+1;;" after enable_live_testing
- Problem: Requires external coordination; clients often miss this
- Benefit: Minimal code change, explicit user action
Recommended: Option A + clearer UX
- Add effect handler for discovery request
- Wire discovery to session RPC
- Update MCP response to include progress-polling guidance
File: C:\Code\Repos\SageFs\scripts\smoke-test.ps1
Lines 325-347: Live Testing Enable + Prime Eval
`powershell
Write-Host " Enabling live testing..." -ForegroundColor DarkGray Invoke-RestMethod -Method Post -Uri "/api/live-testing/enable" -TimeoutSec 5
Write-Host " Priming live testing with a no-op eval..." -ForegroundColor DarkGray = [PSCustomObject]@{ code = "1+1;;" working_directory = } | ConvertTo-Json
= Invoke-RestMethod -Method Post -Uri "/exec"
-Body
-ContentType "application/json"
-TimeoutSec 15
`
Lines 349-375: Discovery Polling
- Polls /api/live-testing/status every 2 seconds for 30 seconds max
- Waits for DiscoveryState in ["ready_with_tests", "ready_zero_tests", "disabled"]
- Falls back to discovered count > 0 if state field missing
Key: Test priming is EXPLICIT and NECESSARY. Without eval, discovery doesn't start.
Test 1: "enable_live_testing on cold session with no tests triggers discovery effect"
- Setup: Empty session, Active=false, DiscoveredTests=[]
- Action: Dispatch EnableLiveTesting
- Assert: Effects include a discovery-triggering signal or eventual transition to Discovering state
- File: LiveTestingDiscoveryStateTests.fs
Test 2: "get_live_test_status DiscoveryRequiresEval is true until discovery completes"
- Setup: Active session, no discovered tests, no LastDiscoveryTime
- Action: Call getLiveTestStatus
- Assert: JSON DiscoveryRequiresEval=true, DiscoveryState="discovering"
- File: LiveTestingDiscoveryStateTests.fs (already exists, line 108)
Test 3: "cold session with enable_live_testing should expose synthetic keystroke entry point"
- Setup: Session created, EnableLiveTesting dispatched, DiscoveredTests empty
- Action: Verify that no keystroke/file-change is required to transition from Discovering
- Assert: DiscoveryState eventually → ReadyZeroTests or ReadyWithTests without user input
- File: LiveTestingDiscoveryStateTests.fs (NEW)
Test 4: "enable_live_testing response differs between warm (tests cached) and cold (empty)"
- Setup: Two contexts, one with cached tests, one empty
- Action: Call setLiveTesting on both
- Assert: Warm returns count; cold returns "requires no-op eval" or equivalent
- File: LiveTestingDiscoveryStateTests.fs (already exists, line 99)
Test 5: "smoke-test priming eval triggers discovery"
- Setup: Fresh session, enabled live testing
- Action: Execute "1+1;;"
- Assert: Discovery state transitions from Discovering to ReadyZeroTests/ReadyWithTests
- File: Tier0CorrectnessTests.fs or HttpApiIntegrationTests.fs (NEW)
| Component | File | Type/Function | Current Behavior |
|---|---|---|---|
| MCP Tool | McpTools.fs:~1190 | �nable_live_testing() | Dispatches EnableLiveTesting msg |
| MCP Impl | Mcp.fs:1643 | setLiveTesting | Returns async; notes cold priming |
| Elm Update | SageFsApp.fs:798 | EnableLiveTesting | No-op if tests empty (ISSUE) |
| Discovery State | LiveTestingTypes.fs:1133 | LiveTestDiscoveryState | Tracks Disabled/Discovering/Ready* |
| Priming Check | LiveTestingTypes.fs:1157 | ||
| equiresPrimingEval | TRUE if Active ∧ empty ∧ never-run | ||
| Status API | Mcp.fs:1557 | getLiveTestStatus | Exposes DiscoveryRequiresEval flag |
| Trace API | Mcp.fs:1738 | getTestTrace | Exposes DiscoveryState + Hint |
| Smoke Test | smoke-test.ps1:325-347 | Priming section | Sends "1+1;;" after enable |
| Test Suite | LiveTestingDiscoveryStateTests.fs | 9 tests | Covers state transitions + MCP semantics |