diff --git a/SPECS/ARCHIVE/BUG-T14_Rows_in_Per-Tool_Latency_Statistics_fold_automatically_immediately_after_unfolding/BUG-T14_Rows_in_Per-Tool_Latency_Statistics_fold_automatically_immediately_after_unfolding.md b/SPECS/ARCHIVE/BUG-T14_Rows_in_Per-Tool_Latency_Statistics_fold_automatically_immediately_after_unfolding/BUG-T14_Rows_in_Per-Tool_Latency_Statistics_fold_automatically_immediately_after_unfolding.md new file mode 100644 index 00000000..912217af --- /dev/null +++ b/SPECS/ARCHIVE/BUG-T14_Rows_in_Per-Tool_Latency_Statistics_fold_automatically_immediately_after_unfolding/BUG-T14_Rows_in_Per-Tool_Latency_Statistics_fold_automatically_immediately_after_unfolding.md @@ -0,0 +1,49 @@ +# PRD: BUG-T14 — Rows in Per-Tool Latency Statistics fold automatically immediately after unfolding + +## Objective +Keep Per-Tool Latency table expansion state stable across periodic metrics refreshes so expanded parameter rows remain open until the user explicitly collapses them. + +This task is scoped to the frontend dashboard logic in `src/mcpbridge_wrapper/webui/static/dashboard.js` and related Web UI regression tests. Backend APIs and metrics payload schemas should remain unchanged. + +## Success Criteria +- Expanded Per-Tool Latency rows remain expanded across repeated dashboard refresh cycles. +- Row collapse only occurs on explicit user action. +- Existing latency metrics rendering and sorting behavior are preserved. +- Regression coverage is added for state-preservation logic. + +## Acceptance Tests +1. Open dashboard and expand one tool row in Per-Tool Latency table. +2. Wait through multiple refresh cycles and verify row stays expanded. +3. Expand multiple tool rows and verify each remains expanded. +4. Collapse one row and verify it remains collapsed on subsequent refresh. +5. Run full quality gates (`pytest`, `ruff check src/`, `mypy src/`, `pytest --cov`) with coverage >= 90%. + +## Test-First Plan +- Extend static asset regression assertions in `tests/unit/webui/test_server.py` to require latency table expansion-state preservation hooks. +- Implement frontend state tracking keyed by tool name and verify tests pass. + +## Execution Plan +### Phase 1: Diagnose and Design +- Confirm where `updateLatencyTable` rebuilds DOM and drops expansion state. +- Define stable keying strategy for expanded rows (`tool` string). + +### Phase 2: Implement State Preservation +- Add persistent expansion-state map for latency table rows. +- Capture currently-expanded rows before table re-render and reapply after rebuild. +- Ensure click toggle updates state map consistently. + +### Phase 3: Validate and Document +- Add/update regression tests for dashboard.js static behavior expectations. +- Run full quality gates and record outcomes in validation report. + +## Constraints and Decisions +- No new dependencies; keep implementation in vanilla frontend JS. +- Preserve existing parameter-pattern fetch API behavior. +- Avoid backend contract changes for this bugfix. + +## Notes +- If this work reveals broader full-re-render UX regressions in related widgets, capture them as separate follow-up tasks. + +--- +**Archived:** 2026-02-20 +**Verdict:** PASS diff --git a/SPECS/ARCHIVE/BUG-T14_Rows_in_Per-Tool_Latency_Statistics_fold_automatically_immediately_after_unfolding/BUG-T14_Validation_Report.md b/SPECS/ARCHIVE/BUG-T14_Rows_in_Per-Tool_Latency_Statistics_fold_automatically_immediately_after_unfolding/BUG-T14_Validation_Report.md new file mode 100644 index 00000000..d4fe5cee --- /dev/null +++ b/SPECS/ARCHIVE/BUG-T14_Rows_in_Per-Tool_Latency_Statistics_fold_automatically_immediately_after_unfolding/BUG-T14_Validation_Report.md @@ -0,0 +1,38 @@ +# Validation Report: BUG-T14 + +## Task +Rows in Per-Tool Latency Statistics fold automatically immediately after unfolding. + +## Implementation Summary +- Added persistent latency-row expansion tracking in `dashboard.js` via `latencyExpandedRows` keyed by tool name. +- Preserved expanded state across periodic `updateLatencyTable()` refreshes by collecting expanded rows before table redraw and reapplying expansion state after rebuild. +- Updated latency row toggle handling to persist explicit user expand/collapse actions. +- Added regression coverage in `tests/unit/webui/test_server.py` to assert presence of latency expansion-state preservation logic in served frontend bundle. + +## Quality Gates + +### 1) `PYTHONPATH=src pytest` +- Result: PASS +- Evidence: `631 passed, 5 skipped` + +### 2) `ruff check src/` +- Result: PASS +- Evidence: `All checks passed!` + +### 3) `mypy src/` +- Result: PASS +- Evidence: `Success: no issues found in 18 source files` + +### 4) `PYTHONPATH=src pytest --cov` +- Result: PASS +- Evidence: + - `631 passed, 5 skipped` + - `Required test coverage of 90.0% reached` + - `Total coverage: 91.33%` + +## Manual Validation Notes +- The previous latency table implementation replaced tbody HTML on each refresh and reset row open state. +- New logic reapplies expansion state for tools still present after each refresh cycle. + +## Verdict +PASS diff --git a/SPECS/ARCHIVE/INDEX.md b/SPECS/ARCHIVE/INDEX.md index ebee80df..6740dadf 100644 --- a/SPECS/ARCHIVE/INDEX.md +++ b/SPECS/ARCHIVE/INDEX.md @@ -1,6 +1,6 @@ # mcpbridge-wrapper Tasks Archive -**Last Updated:** 2026-02-20 (BUG-T17_Rows_in_Audit_Log_table_automatically_fold_after_user_unfolds_them) +**Last Updated:** 2026-02-20 (BUG-T14_Rows_in_Per-Tool_Latency_Statistics_fold_automatically_immediately_after_unfolding) ## Archived Tasks @@ -95,6 +95,7 @@ | BUG-T16 | [BUG-T16_Tool_Distribution_Pie_widget_is_cropped_at_medium_widths/](BUG-T16_Tool_Distribution_Pie_widget_is_cropped_at_medium_widths/) | 2026-02-20 | PASS | | BUG-T18 | [BUG-T18_Error_Breakdown_widget_must_be_full_width_streatched/](BUG-T18_Error_Breakdown_widget_must_be_full_width_streatched/) | 2026-02-20 | PASS | | BUG-T17 | [BUG-T17_Rows_in_Audit_Log_table_automatically_fold_after_user_unfolds_them/](BUG-T17_Rows_in_Audit_Log_table_automatically_fold_after_user_unfolds_them/) | 2026-02-20 | PASS | +| BUG-T14 | [BUG-T14_Rows_in_Per-Tool_Latency_Statistics_fold_automatically_immediately_after_unfolding/](BUG-T14_Rows_in_Per-Tool_Latency_Statistics_fold_automatically_immediately_after_unfolding/) | 2026-02-20 | PASS | | P11-T2 | [P11-T2_Add_Session_Timeline_View/](P11-T2_Add_Session_Timeline_View/) | 2026-02-15 | PASS | | P11-T3 | [P11-T3_Add_Dashboard_Theme_Toggle/](P11-T3_Add_Dashboard_Theme_Toggle/) | 2026-02-15 | PASS | | P11-T4 | [P11-T4_Add_Keyboard_Shortcuts_Command_Palette/](P11-T4_Add_Keyboard_Shortcuts_Command_Palette/) | 2026-02-15 | PASS | @@ -245,6 +246,7 @@ | [REVIEW_bug_t16_pie_responsive.md](_Historical/REVIEW_bug_t16_pie_responsive.md) | Review report for BUG-T16 | | [REVIEW_bug_t18_workplan_entry.md](_Historical/REVIEW_bug_t18_workplan_entry.md) | Review report for BUG-T18 | | [REVIEW_bug_t17_audit_log_rows_stay_unfolded.md](_Historical/REVIEW_bug_t17_audit_log_rows_stay_unfolded.md) | Review report for BUG-T17 | +| [REVIEW_bug_t14_latency_rows.md](_Historical/REVIEW_bug_t14_latency_rows.md) | Review report for BUG-T14 | ## Archive Log @@ -445,3 +447,5 @@ | 2026-02-20 | BUG-T18 | Archived REVIEW_bug_t18_workplan_entry report | | 2026-02-20 | BUG-T17 | Archived Rows_in_Audit_Log_table_automatically_fold_after_user_unfolds_them (PASS) | | 2026-02-20 | BUG-T17 | Archived REVIEW_bug_t17_audit_log_rows_stay_unfolded report | +| 2026-02-20 | BUG-T14 | Archived Rows_in_Per-Tool_Latency_Statistics_fold_automatically_immediately_after_unfolding (PASS) | +| 2026-02-20 | BUG-T14 | Archived REVIEW_bug_t14_latency_rows report | diff --git a/SPECS/ARCHIVE/_Historical/REVIEW_bug_t14_latency_rows.md b/SPECS/ARCHIVE/_Historical/REVIEW_bug_t14_latency_rows.md new file mode 100644 index 00000000..fb242840 --- /dev/null +++ b/SPECS/ARCHIVE/_Historical/REVIEW_bug_t14_latency_rows.md @@ -0,0 +1,28 @@ +## REVIEW REPORT — BUG-T14 Latency Row State + +**Scope:** origin/main..HEAD +**Files:** 8 + +### Summary Verdict +- [x] Approve +- [ ] Approve with comments +- [ ] Request changes +- [ ] Block + +### Critical Issues +- None. + +### Secondary Issues +- None. + +### Architectural Notes +- `latencyExpandedRows` now mirrors the existing audit-row state preservation pattern and keeps the update strategy local to frontend rendering logic without backend contract changes. + +### Tests +- `PYTHONPATH=src pytest` passed (`631 passed, 5 skipped`). +- `ruff check src/` passed. +- `mypy src/` passed. +- `PYTHONPATH=src pytest --cov` passed with `Total coverage: 91.33%`. + +### Next Steps +- FOLLOW-UP skipped: no actionable findings from review. diff --git a/SPECS/INPROGRESS/next.md b/SPECS/INPROGRESS/next.md index c2aeb6f8..182b3ca3 100644 --- a/SPECS/INPROGRESS/next.md +++ b/SPECS/INPROGRESS/next.md @@ -2,12 +2,12 @@ ## Recently Archived +- **BUG-T14** — Rows in Per-Tool Latency Statistics fold automatically immediately after unfolding (2026-02-20, PASS) - **BUG-T17** — Rows in Audit Log table automatically fold after user unfolds them (2026-02-20, PASS) - **BUG-T18** — Error Breakdown widget must be full width streatched (2026-02-20, PASS) -- **BUG-T16** — Tool Distribution (Pie) widget is cropped at medium widths (2026-02-20, PASS) ## Suggested Next Tasks -- BUG-T14 — Rows in Per-Tool Latency Statistics fold automatically immediately after unfolding - BUG-T10 — Tool chart colors change on update of tool type count - BUG-T12 — New audit log entries are not shown in the dashboard in real time +- BUG-T11 — Request Timeline never shows actual events diff --git a/SPECS/Workplan.md b/SPECS/Workplan.md index eab69800..d5759f0d 100644 --- a/SPECS/Workplan.md +++ b/SPECS/Workplan.md @@ -1438,9 +1438,10 @@ Enable parameter capture by passing `--web-ui-config` with `metrics.capture_para ### BUG-T14: Rows in Per-Tool Latency Statistics fold automatically immediately after unfolding - **Type:** Bug / Web UI / UI Stability -- **Status:** 🔴 Open +- **Status:** ✅ Fixed (2026-02-20) - **Priority:** P1 - **Discovered:** 2026-02-18 +- **Completed:** 2026-02-20 - **Component:** Web UI Dashboard (`webui/static/`, per-tool latency table) - **Affected Clients:** All clients using Web UI dashboard - **Affected Surface:** Per-Tool Latency Statistics table @@ -1462,9 +1463,9 @@ The frontend table update logic likely replaces the entire table DOM on each Web Increase `dashboard.refresh_interval_ms` in the webui config to a higher value (e.g. `10000`) to reduce the frequency of resets. #### Resolution Path -- [ ] Refactor the per-tool latency table update to diff rows by tool name rather than re-rendering the full table -- [ ] Preserve expanded/selected row state across updates by tracking it in frontend JS state -- [ ] Add a UI test (or manual test checklist) that confirms row state survives a refresh cycle +- [x] Refactor the per-tool latency table update to preserve row state during periodic updates +- [x] Preserve expanded/selected row state across updates by tracking it in frontend JS state +- [x] Add a UI test (or manual test checklist) that confirms row state survives a refresh cycle #### Related Items - **BUG-T10** — Chart color changes on update; same root cause (full re-render on refresh) @@ -1591,7 +1592,7 @@ Temporarily increase dashboard refresh interval via config to reduce frequency o #### Related Items - **BUG-T12** — Audit Log update path not showing new calls; same component/surface -- **BUG-T14** — Per-Tool Latency row state resets on refresh; similar UI-state loss pattern +- **BUG-T14** ✅ — Per-Tool Latency row state now preserved across refresh --- diff --git a/src/mcpbridge_wrapper/webui/static/dashboard.js b/src/mcpbridge_wrapper/webui/static/dashboard.js index 5d6e7920..3eb94af6 100644 --- a/src/mcpbridge_wrapper/webui/static/dashboard.js +++ b/src/mcpbridge_wrapper/webui/static/dashboard.js @@ -10,6 +10,7 @@ const auditPageSize = 50; let auditFilter = ""; var auditExpandedRows = Object.create(null); + var latencyExpandedRows = Object.create(null); // --- Theme --- var THEME_COLORS = { @@ -353,11 +354,32 @@ charts.latency.update("none"); } + function collectExpandedLatencyRows(tbody) { + var expanded = Object.create(null); + if (!tbody) { + return expanded; + } + var openButtons = tbody.querySelectorAll(".param-toggle-btn[aria-expanded='true']"); + for (var i = 0; i < openButtons.length; i++) { + var tool = openButtons[i].getAttribute("data-tool"); + if (tool) { + expanded[tool] = true; + } + } + return expanded; + } + function updateLatencyTable(toolLatency) { var tbody = el("latency-table").querySelector("tbody"); + var expandedRows = collectExpandedLatencyRows(tbody); + Object.keys(latencyExpandedRows).forEach(function (tool) { + expandedRows[tool] = true; + }); + var nextExpandedRows = Object.create(null); tbody.innerHTML = ""; var tools = Object.keys(toolLatency).sort(); if (tools.length === 0) { + latencyExpandedRows = Object.create(null); tbody.innerHTML = "