story-kit: queue 101_story_test_coverage_http_chat_rs_to_80 for merge

This commit is contained in:
Dave
2026-02-23 22:00:33 +00:00
parent f45bc13522
commit a759a59a46
5 changed files with 92 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
---
name: "Web UI SSE socket stops updating after a while"
---
# Bug 114: Web UI SSE socket stops updating after a while
## Description
After the first several pipeline updates, the UI stops reflecting changes. Lozenges stop flying, stories stop moving between stages, but the server is still advancing the pipeline fine. A page refresh fixes it.
The root cause is likely not the SSE transport itself but rather the large combined pipeline state push that the frontend subscribes to. Investigate the SSE event handler in the frontend client — it receives a single big `pipeline_state` event that everything listens to. Something may be going wrong in the processing/diffing of that state after several rapid updates.
## Investigation hints
- Start with the SSE client in `frontend/src/api/client.ts` — look at `onPipelineState` handling
- Check if the SSE connection is actually dropping (add a log on close/error) or if events arrive but stop being processed
- The `LozengeFlyContext` diffing logic in `useLayoutEffect` compares prev vs current pipeline — could a stale ref or missed update break the chain?
- Server-side: the `broadcast::channel` has a 1024 buffer — if a slow consumer lags, tokio drops it silently
## How to Reproduce
1. Open the web UI
2. Start several agents working on stories
3. Wait 10-20 minutes while agents complete and pipeline advances
4. Observe that the UI stops reflecting pipeline changes
5. Refresh the page — state is correct again
## Actual Result
UI freezes showing stale pipeline state after several updates.
## Expected Result
UI should always reflect current pipeline state in real time without needing a manual refresh.
## Acceptance Criteria
- [ ] Root cause identified (SSE transport vs frontend state processing)
- [ ] Fix implemented with auto-recovery if connection drops
- [ ] UI stays live through sustained agent activity (20+ minutes)

View File

@@ -140,4 +140,56 @@ mod tests {
let val = store2.get(EDITOR_COMMAND_KEY);
assert_eq!(val, Some(json!("zed")));
}
#[tokio::test]
async fn get_editor_http_handler_returns_null_when_not_set() {
let dir = TempDir::new().unwrap();
let ctx = test_ctx(&dir);
let api = SettingsApi {
ctx: Arc::new(ctx),
};
let result = api.get_editor().await.unwrap().0;
assert!(result.editor_command.is_none());
}
#[tokio::test]
async fn set_editor_http_handler_stores_value() {
let dir = TempDir::new().unwrap();
let ctx = test_ctx(&dir);
let api = SettingsApi {
ctx: Arc::new(ctx),
};
let result = api
.set_editor(Json(EditorCommandPayload {
editor_command: Some("zed".to_string()),
}))
.await
.unwrap()
.0;
assert_eq!(result.editor_command, Some("zed".to_string()));
}
#[tokio::test]
async fn set_editor_http_handler_clears_value_when_null() {
let dir = TempDir::new().unwrap();
let ctx = test_ctx(&dir);
let api = SettingsApi {
ctx: Arc::new(ctx),
};
// First set a value
api.set_editor(Json(EditorCommandPayload {
editor_command: Some("code".to_string()),
}))
.await
.unwrap();
// Now clear it
let result = api
.set_editor(Json(EditorCommandPayload {
editor_command: None,
}))
.await
.unwrap()
.0;
assert!(result.editor_command.is_none());
}
}