fix(central-ui): Flow Debugger prediction no longer deadlocks the Blazor circuit

FlowDebugService.PredictMethodAsync resolved route targets with a sync-over-async
GetAwaiter().GetResult() inside the predictor's synchronous resolver — on the circuit's
single-threaded renderer sync context that blocked the dispatcher forever the moment a
method whose script routes to a site script was chosen (every later click and JS-interop
reply queued behind it; the page looked inert with a healthy websocket and no error).
Misdiagnosed on 2026-09-06 as a login-disabled localStorage stall. Now a resolve-and-rewalk
loop: each pass records the targets it could not answer, they are awaited between passes,
and the walk converges once a pass asks for nothing new (bounded by MaxPredictionPasses).

Regression test drives the service under a single-thread pump sync context (fails in 10 s
on the old code, passes now) plus a nested Scripts.Call resolve-across-passes test.

Claude-Session: https://claude.ai/code/session_01NcMG6xXdsJbmD2wYAgkK29
This commit is contained in:
Joseph Doherty
2026-09-06 10:57:09 -04:00
parent 5394872040
commit 4769e192e3
3 changed files with 249 additions and 33 deletions
@@ -1,17 +1,94 @@
{
"planPath": "docs/plans/2026-09-06-flow-debugger-followups.md",
"tasks": [
{"id": 0, "subject": "Task 0: Diagnose the login-disabled interop stall (investigation)", "status": "pending"},
{"id": 1, "subject": "Task 1: FlowStep.CorrelationId + CLI column", "status": "pending", "blockedBy": [0]},
{"id": 2, "subject": "Task 2: Site Calls query — filter by TrackedOperationId", "status": "pending", "blockedBy": [0]},
{"id": 3, "subject": "Task 3: Notification Outbox query — filter by NotificationId", "status": "pending", "blockedBy": [0]},
{"id": 4, "subject": "Task 4: Site Calls page accepts ?correlationId=", "status": "pending", "blockedBy": [2]},
{"id": 5, "subject": "Task 5: Notification report page accepts ?correlationId=", "status": "pending", "blockedBy": [3]},
{"id": 6, "subject": "Task 6: FlowStepDetail — report deep links + Retry/Discard controls", "status": "pending", "blockedBy": [1]},
{"id": 7, "subject": "Task 7: IFlowDebugService.RetryOutboundAsync / DiscardOutboundAsync", "status": "pending", "blockedBy": [1]},
{"id": 8, "subject": "Task 8: Flow Debugger page — wire actions, confirm, scope check, re-poll", "status": "pending", "blockedBy": [6, 7]},
{"id": 9, "subject": "Task 9: Playwright — browser Live run + localStorage restore (rig)", "status": "pending", "blockedBy": [8, 4, 5]},
{"id": 10, "subject": "Task 10: Docs, register, memory", "status": "pending", "blockedBy": [8]}
{
"id": 0,
"subject": "Task 0: Diagnose the login-disabled interop stall (investigation)",
"status": "completed"
},
{
"id": 1,
"subject": "Task 1: FlowStep.CorrelationId + CLI column",
"status": "pending",
"blockedBy": [
0
]
},
{
"id": 2,
"subject": "Task 2: Site Calls query \u2014 filter by TrackedOperationId",
"status": "pending",
"blockedBy": [
0
]
},
{
"id": 3,
"subject": "Task 3: Notification Outbox query \u2014 filter by NotificationId",
"status": "pending",
"blockedBy": [
0
]
},
{
"id": 4,
"subject": "Task 4: Site Calls page accepts ?correlationId=",
"status": "pending",
"blockedBy": [
2
]
},
{
"id": 5,
"subject": "Task 5: Notification report page accepts ?correlationId=",
"status": "pending",
"blockedBy": [
3
]
},
{
"id": 6,
"subject": "Task 6: FlowStepDetail \u2014 report deep links + Retry/Discard controls",
"status": "pending",
"blockedBy": [
1
]
},
{
"id": 7,
"subject": "Task 7: IFlowDebugService.RetryOutboundAsync / DiscardOutboundAsync",
"status": "pending",
"blockedBy": [
1
]
},
{
"id": 8,
"subject": "Task 8: Flow Debugger page \u2014 wire actions, confirm, scope check, re-poll",
"status": "pending",
"blockedBy": [
6,
7
]
},
{
"id": 9,
"subject": "Task 9: Playwright \u2014 browser Live run + localStorage restore (rig)",
"status": "pending",
"blockedBy": [
8,
4,
5
]
},
{
"id": 10,
"subject": "Task 10: Docs, register, memory",
"status": "pending",
"blockedBy": [
8
]
}
],
"lastUpdated": "2026-09-06T00:00:00Z"
}
"lastUpdated": "2026-09-06T15:12:00Z"
}