diff --git a/SPECS/ARCHIVE/BUG-T12_Audit_Log_does_not_show_new_calls/BUG-T12_Audit_Log_does_not_show_new_calls.md b/SPECS/ARCHIVE/BUG-T12_Audit_Log_does_not_show_new_calls/BUG-T12_Audit_Log_does_not_show_new_calls.md new file mode 100644 index 00000000..8957392f --- /dev/null +++ b/SPECS/ARCHIVE/BUG-T12_Audit_Log_does_not_show_new_calls/BUG-T12_Audit_Log_does_not_show_new_calls.md @@ -0,0 +1,68 @@ +# PRD: BUG-T12 — Audit Log does not show new calls + +**Task ID:** BUG-T12 +**Priority:** P1 +**Status:** In Progress +**Owner:** Codex (flow-run) +**Dependencies:** BUG-T8 (shared audit visibility) + +## 1. Problem Statement + +The Web UI Audit Log table does not show newly recorded MCP tool calls while the dashboard is open. Users only see initial rows and cannot monitor live activity. + +## 2. Scope + +### In scope +- Trace backend-to-frontend update path for audit entries. +- Fix the issue so newly created audit entries are visible in the dashboard without manual page refresh. +- Add regression coverage for backend and frontend update behavior. + +### Out of scope +- New audit storage backends or schema redesign. +- Dashboard visual redesign unrelated to data freshness. +- Non-audit widgets. + +## 3. Deliverables + +- Backend/frontend code updates required to propagate and render new audit entries. +- Tests validating new entries are emitted and displayed by update logic. +- `SPECS/INPROGRESS/BUG-T12_Validation_Report.md` with quality-gate results. + +## 4. Technical Plan + +1. Reproduce expected/actual behavior via existing tests and code-path inspection. +2. Inspect audit collection path: + - `AuditLogger` write path + - `/api/audit` response generation + - WebSocket or polling update payloads +3. Inspect frontend audit table refresh logic and row patch/append strategy. +4. Implement minimal fix preserving existing behavior for expanded-row state and refresh cadence. +5. Add/adjust tests: + - Backend/API test that new audit entries are returned. + - Frontend/unit test for rendering update from new payload. +6. Run quality gates and document evidence in validation report. + +## 5. Acceptance Criteria + +- [ ] New tool calls appear in the Audit Log table during an active dashboard session. +- [ ] `/api/audit` returns newly created entries after calls complete. +- [ ] Existing audit row-state behavior regressions are not reintroduced. +- [ ] Targeted regression tests added/updated and passing. +- [ ] Full quality gates pass (`pytest`, `ruff check src/`, `mypy src/`, `pytest --cov` with >=90% coverage). + +## 6. Risks and Mitigations + +- Risk: Fixing refresh logic may reintroduce row folding regressions. + - Mitigation: Preserve ID-based incremental updates and keep existing row-state tests green. +- Risk: Backend cache or ordering changes may affect export endpoints. + - Mitigation: Keep API schema unchanged and run integration tests around audit endpoints. + +## 7. Validation Strategy + +- Run focused tests for webui audit modules first. +- Run full repository quality gates. +- Capture command outputs and PASS/FAIL verdict in validation report. + +--- +**Archived:** 2026-02-20 +**Verdict:** PASS diff --git a/SPECS/ARCHIVE/BUG-T12_Audit_Log_does_not_show_new_calls/BUG-T12_Validation_Report.md b/SPECS/ARCHIVE/BUG-T12_Audit_Log_does_not_show_new_calls/BUG-T12_Validation_Report.md new file mode 100644 index 00000000..e34eebcc --- /dev/null +++ b/SPECS/ARCHIVE/BUG-T12_Audit_Log_does_not_show_new_calls/BUG-T12_Validation_Report.md @@ -0,0 +1,46 @@ +# Validation Report: BUG-T12 + +**Task:** BUG-T12 — Audit Log does not show new calls +**Date:** 2026-02-20 +**Branch:** feature/BUG-T12-audit-log-does-not-show-new-calls + +## Summary + +Implemented a dashboard-side audit refresh fix so new audit entries appear promptly during live traffic by: +- Triggering audit reloads from live `metrics_update` events when `total_requests` changes. +- Disabling browser cache for `/api/audit` fetches and appending a timestamp query param. +- Ignoring stale in-flight audit fetch responses to prevent old payloads from overwriting newer rows. + +## Files Changed + +- `src/mcpbridge_wrapper/webui/static/dashboard.js` +- `tests/unit/webui/test_server.py` + +## Acceptance Criteria + +- [x] New tool calls appear in the Audit Log table during an active dashboard session. +- [x] `/api/audit` returns newly created entries after calls complete. +- [x] Existing audit row-state behavior regressions are not reintroduced. +- [x] Targeted regression tests added/updated and passing. +- [x] Full quality gates pass (`pytest`, `ruff check src/`, `mypy src/`, `pytest --cov` with >=90% coverage). + +## Quality Gates + +1. `PYTHONPATH=src pytest tests/unit/webui/test_server.py -q` + Result: **PASS** (42 passed) + +2. `PYTHONPATH=src ruff check src/` + Result: **PASS** (All checks passed) + +3. `PYTHONPATH=src mypy src/` + Result: **PASS** (Success: no issues found in 18 source files) + +4. `PYTHONPATH=src pytest` + Result: **PASS** (633 passed, 5 skipped) + +5. `PYTHONPATH=src pytest --cov` + Result: **PASS** (coverage 91.33%, threshold >= 90%) + +## Notes + +- Existing warnings from `websockets.legacy` deprecation remain unchanged and are outside BUG-T12 scope. diff --git a/SPECS/ARCHIVE/BUG-T12_Audit_Log_does_not_show_new_calls/REVIEW_BUG-T12_audit_log_live_updates.md b/SPECS/ARCHIVE/BUG-T12_Audit_Log_does_not_show_new_calls/REVIEW_BUG-T12_audit_log_live_updates.md new file mode 100644 index 00000000..23847ce6 --- /dev/null +++ b/SPECS/ARCHIVE/BUG-T12_Audit_Log_does_not_show_new_calls/REVIEW_BUG-T12_audit_log_live_updates.md @@ -0,0 +1,28 @@ +## REVIEW REPORT — BUG-T12 audit log live updates + +**Scope:** origin/main..HEAD +**Files:** 7 + +### Summary Verdict +- [x] Approve +- [ ] Approve with comments +- [ ] Request changes +- [ ] Block + +### Critical Issues +- None. + +### Secondary Issues +- None. + +### Architectural Notes +- Frontend now refreshes audit data on live `metrics_update` request-count changes in addition to periodic polling. +- Added cache-busting + `no-store` for `/api/audit` requests and stale-response suppression to avoid out-of-order overwrite of newer rows. +- Existing row expansion-state preservation logic remains intact. + +### Tests +- Added JS assertions in unit tests for live-refresh and cache bypass behavior. +- Full quality gates passed, including `pytest --cov` at 91.33% (>= 90%). + +### Next Steps +- FOLLOW-UP skipped: no actionable review findings. diff --git a/SPECS/ARCHIVE/INDEX.md b/SPECS/ARCHIVE/INDEX.md index 67dd5695..ca29bdaf 100644 --- a/SPECS/ARCHIVE/INDEX.md +++ b/SPECS/ARCHIVE/INDEX.md @@ -1,11 +1,12 @@ # mcpbridge-wrapper Tasks Archive -**Last Updated:** 2026-02-20 (BUG-T10_Tool_chart_colors_change_on_update_of_tool_type_count) +**Last Updated:** 2026-02-20 (BUG-T12_Audit_Log_does_not_show_new_calls) ## Archived Tasks | Task ID | Folder | Archived | Verdict | |---------|--------|----------|---------| +| BUG-T12 | [BUG-T12_Audit_Log_does_not_show_new_calls/](BUG-T12_Audit_Log_does_not_show_new_calls/) | 2026-02-20 | PASS | | BUG-T10 | [BUG-T10_Tool_chart_colors_change_on_update_of_tool_type_count/](BUG-T10_Tool_chart_colors_change_on_update_of_tool_type_count/) | 2026-02-20 | PASS | | P1-T1 | [P1-T1_Create_project_directory_structure/](P1-T1_Create_project_directory_structure/) | 2026-02-07 | PASS | | P1-T2 | [P1-T2_Initialize_Python_project_with_pyproject.toml/](P1-T2_Initialize_Python_project_with_pyproject.toml/) | 2026-02-07 | PASS | @@ -250,11 +251,14 @@ | [REVIEW_bug_t14_latency_rows.md](_Historical/REVIEW_bug_t14_latency_rows.md) | Review report for BUG-T14 | | [REVIEW_bug_t10.md](_Historical/REVIEW_bug_t10.md) | Review report for BUG-T10 | +| [REVIEW_BUG-T12_audit_log_live_updates.md](BUG-T12_Audit_Log_does_not_show_new_calls/REVIEW_BUG-T12_audit_log_live_updates.md) | Review report for BUG-T12 | ## Archive Log | Date | Task ID | Action | |------|---------|--------| +| 2026-02-20 | BUG-T12 | Archived REVIEW_BUG-T12_audit_log_live_updates report | +| 2026-02-20 | BUG-T12 | Archived Audit_Log_does_not_show_new_calls (PASS) | | 2026-02-20 | BUG-T10 | Archived REVIEW_bug_t10 report | | 2026-02-20 | BUG-T10 | Archived Tool_chart_colors_change_on_update_of_tool_type_count (PASS) | | 2026-02-07 | P1-T1 | Archived with PASS verdict | diff --git a/SPECS/INPROGRESS/next.md b/SPECS/INPROGRESS/next.md index 11cd1269..a00014bd 100644 --- a/SPECS/INPROGRESS/next.md +++ b/SPECS/INPROGRESS/next.md @@ -2,12 +2,12 @@ ## Recently Archived -- **BUG-T10** — Tool chart colors change on update of tool type count (2026-02-20, PASS) -- **BUG-T14** — Rows in Per-Tool Latency Statistics fold automatically immediately after unfolding (2026-02-20, PASS) +- **BUG-T12** — Audit Log does not show new calls (2026-02-20, PASS) - **BUG-T17** — Rows in Audit Log table automatically fold after user unfolds them (2026-02-20, PASS) +- **BUG-T14** — Rows in Per-Tool Latency Statistics fold automatically immediately after unfolding (2026-02-20, PASS) ## Suggested Next Tasks -- BUG-T12 — New audit log entries are not shown in the dashboard in real time -- BUG-T11 — Request Timeline never shows actual events +- BUG-T11 — Chart Request Timeline never shows actual events - BUG-T13 — Per-Tool Latency Statistics does not show params when `capture_params` is false +- BUG-T18 — Error Breakdown widget must be full width streatched diff --git a/SPECS/Workplan.md b/SPECS/Workplan.md index da94f988..e6d6ef82 100644 --- a/SPECS/Workplan.md +++ b/SPECS/Workplan.md @@ -1355,7 +1355,7 @@ None. The chart is non-functional for monitoring purposes. Users must rely on th ### BUG-T12: Audit Log does not show new calls - **Type:** Bug / Web UI / Audit Log -- **Status:** 🔴 Open +- **Status:** ✅ Fixed (2026-02-20) - **Priority:** P1 - **Discovered:** 2026-02-18 - **Component:** Web UI Dashboard (`webui/static/`, audit log table) @@ -1632,6 +1632,85 @@ None. --- +### BUG-T19: Audit Log and Session Timeline are inconsistent with tool charts in multi-process runs +- **Type:** Bug / Web UI / Data Consistency +- **Status:** 🔴 Open +- **Priority:** P1 +- **Discovered:** 2026-02-20 +- **Component:** Web UI backend (`webui/server.py`, `webui/audit.py`, `webui/shared_metrics.py`) +- **Affected Clients:** Cursor and other short-lived multi-process MCP clients +- **Affected Surface:** Audit Log table, Session Timeline, Tool usage charts + +#### Description +Dashboard surfaces are inconsistent: tool charts show fresh activity while Audit Log and Session Timeline remain stale (or show only old rows such as an earlier `initialize`), especially after reconnecting Cursor. + +#### Symptoms +```text +New tool usage appears in chart widgets. +Audit Log does not add a new row for reconnect/initialize. +Session Timeline still shows older last event. +``` + +#### Root Cause Analysis +Likely split data source model: +- charts/KPIs use `SharedMetricsStore` (cross-process SQLite), +- audit/sessions use process-local `AuditLogger` in-memory entries loaded at startup. +When a different wrapper process receives new events, chart data advances but local audit/session views can lag. + +#### Workaround +Use export endpoints (`/api/audit/export/json` or `/api/audit/export/csv`) for a broader snapshot, but real-time consistency remains unreliable. + +#### Resolution Path +- [ ] Reproduce with repeated Cursor reconnects in a multi-process setup and capture API deltas between `/api/metrics`, `/api/audit`, and `/api/sessions` +- [ ] Choose and implement a single shared source of truth for audit/session data across processes (SQLite-backed audit store or equivalent) +- [ ] Ensure `/api/audit` reflects newly recorded entries regardless of which wrapper process logged them +- [ ] Ensure `/api/sessions` is computed from the same shared data source as Audit Log +- [ ] Add integration regression test covering reconnect + new initialize row visibility in Audit Log and Session Timeline +- [ ] Document consistency guarantees and limitations in `docs/webui-setup.md` and troubleshooting guide + +#### Related Items +- **BUG-T12** ✅ — Audit Log live refresh path improved but did not fully solve cross-process consistency +- **BUG-T8** ✅ — Cross-process audit visibility baseline; likely related implementation surface +- **P10-T2** ✅ — Shared metrics store pattern reference + +--- + +### BUG-T20: Session Timeline can show negative duration due to incorrect entry ordering +- **Type:** Bug / Web UI / Session Analytics +- **Status:** 🔴 Open +- **Priority:** P1 +- **Discovered:** 2026-02-20 +- **Component:** Session detection path (`webui/server.py`, `webui/sessions.py`) +- **Affected Clients:** All clients using Session Timeline +- **Affected Surface:** Session Timeline duration and ordering + +#### Description +Session Timeline can display impossible negative durations (for example, `-174224s`) and stale-looking last events. + +#### Symptoms +```text +Session shows negative duration. +Session start/end ordering appears inverted. +``` + +#### Root Cause Analysis +`detect_sessions()` expects entries sorted by timestamp ascending, but callers pass most-recent-first audit entries, causing inverted session boundaries and invalid duration math. + +#### Workaround +None. + +#### Resolution Path +- [ ] Normalize session input ordering to ascending timestamps before calling `detect_sessions()` +- [ ] Add defensive sorting (or contract enforcement) in session computation path +- [ ] Add regression test asserting non-negative session duration for mixed/newest-first inputs +- [ ] Validate timeline rendering shows monotonic ordering and correct latest event after reconnect/activity + +#### Related Items +- **BUG-T19** — Shared audit/session consistency issue may amplify session timeline staleness +- **P11-T2** ✅ — Session Timeline feature implementation + +--- + ### Phase 10: Web UI Control & Audit Dashboard **Intent:** Create a web-based dashboard for real-time monitoring, control, and audit logging of the XcodeMCPWrapper. Provides visibility into MCP tool usage, performance metrics, and operational control. diff --git a/src/mcpbridge_wrapper/webui/static/dashboard.js b/src/mcpbridge_wrapper/webui/static/dashboard.js index ce6e4491..37111763 100644 --- a/src/mcpbridge_wrapper/webui/static/dashboard.js +++ b/src/mcpbridge_wrapper/webui/static/dashboard.js @@ -11,6 +11,8 @@ let auditFilter = ""; var auditExpandedRows = Object.create(null); var latencyExpandedRows = Object.create(null); + var latestAuditRefreshRequest = 0; + var lastSeenTotalRequests = null; // --- Theme --- var THEME_COLORS = { @@ -621,6 +623,13 @@ if (data.sessions !== undefined) { renderTimeline(data.sessions); } + + // Refresh audit rows when request volume changes so new calls appear quickly. + var totalRequests = data && data.summary ? data.summary.total_requests : null; + if (typeof totalRequests === "number" && totalRequests !== lastSeenTotalRequests) { + lastSeenTotalRequests = totalRequests; + loadAuditLogs(); + } } // --- Audit Detail Panel --- @@ -719,10 +728,15 @@ function loadAuditLogs() { var url = "/api/audit?limit=" + auditPageSize + "&offset=" + (auditPage * auditPageSize); if (auditFilter) url += "&tool=" + encodeURIComponent(auditFilter); + url += "&_ts=" + Date.now(); + var refreshRequestId = ++latestAuditRefreshRequest; - fetch(url) + fetch(url, { cache: "no-store" }) .then(function (r) { return r.json(); }) .then(function (data) { + if (refreshRequestId !== latestAuditRefreshRequest) { + return; + } var tbody = el("audit-table").querySelector("tbody"); var expandedRows = collectExpandedAuditRows(tbody); for (var key in auditExpandedRows) { diff --git a/tests/unit/webui/test_server.py b/tests/unit/webui/test_server.py index 86be847e..4fb0df54 100644 --- a/tests/unit/webui/test_server.py +++ b/tests/unit/webui/test_server.py @@ -206,6 +206,21 @@ def test_dashboard_js_preserves_audit_row_expansion_state(self, client): assert 'tr.setAttribute("data-audit-row-key", rowKey);' in response.text assert "toggleDetailRow(tr, requestId, rowKey, false);" in response.text + def test_dashboard_js_refreshes_audit_log_on_live_request_updates(self, client): + """Audit table refreshes from live metrics updates and bypasses browser cache.""" + response = client.get("/static/dashboard.js") + assert response.status_code == 200 + assert "var latestAuditRefreshRequest = 0;" in response.text + assert "var lastSeenTotalRequests = null;" in response.text + expected_refresh_check = ( + 'if (typeof totalRequests === "number" && totalRequests !== lastSeenTotalRequests)' + ) + assert expected_refresh_check in response.text + assert "loadAuditLogs();" in response.text + assert 'url += "&_ts=" + Date.now();' in response.text + assert 'fetch(url, { cache: "no-store" })' in response.text + assert "if (refreshRequestId !== latestAuditRefreshRequest) {" in response.text + def test_dashboard_js_preserves_latency_row_expansion_state(self, client): """Latency table parameter row state survives periodic table refreshes.""" response = client.get("/static/dashboard.js")