Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
fetch-depth: 0 # Need full history for branch comparison

- name: Check DocC sync
run: make doccheck-all
run: make doccheck-all-strict
lint:
name: Lint & Type Check
runs-on: ubuntu-latest
Expand Down
12 changes: 8 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Makefile for mcpbridge-wrapper

.PHONY: help install install-webui test test-webui lint format format-check typecheck doccheck doccheck-staged doccheck-branch doccheck-all package-assets-check bump-version clean webui webui-health check
.PHONY: help install install-webui test test-webui lint format format-check typecheck doccheck doccheck-staged doccheck-branch doccheck-all doccheck-all-strict package-assets-check bump-version clean webui webui-health check

help:
@echo "Available targets:"
Expand All @@ -16,6 +16,7 @@ help:
@echo " doccheck-staged - Check staged docs/ sync with DocC catalog"
@echo " doccheck-branch - Check docs/ sync against git branch"
@echo " doccheck-all - Check docs/ sync for unstaged, staged, and branch changes"
@echo " doccheck-all-strict - Same as doccheck-all + require doc/docc updates in same commit on branch scope"
@echo " package-assets-check - Build artifacts and verify required packaged assets"
@echo " bump-version - Update pyproject.toml and server.json versions (VERSION=x.y.z, add DRY_RUN=1 to preview)"
@echo " webui - Start wrapper with Web UI dashboard (port 8080)"
Expand Down Expand Up @@ -46,13 +47,13 @@ test-webui:
pytest tests/unit/webui/ tests/integration/webui/ -v --cov=src/mcpbridge_wrapper/webui --cov-report=term-missing

lint:
ruff check src/ tests/
python -m ruff check src/ tests/

format:
ruff format src/ tests/
python -m ruff format src/ tests/

format-check:
ruff format --check src/ tests/
python -m ruff format --check src/ tests/

typecheck:
mypy src/
Expand All @@ -69,6 +70,9 @@ doccheck-branch:
doccheck-all:
python scripts/check_doc_sync.py --all

doccheck-all-strict:
python scripts/check_doc_sync.py --all --require-same-commit

package-assets-check:
python -m build --sdist --wheel
python scripts/check_package_assets.py --dist-dir dist
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# PRD: BUG-T19 — Audit Log and Session Timeline are inconsistent with tool charts in multi-process runs

## Objective
Make Audit Log (`/api/audit`) and Session Timeline (`/api/sessions` + websocket `sessions`) use the same cross-process data source so they stay in sync with chart widgets in multi-process client setups (Cursor/Zed reconnect patterns).

## Background
Current chart/KPI widgets are backed by `SharedMetricsStore` (SQLite, process-shared), while audit/session views are currently sourced from `AuditLogger._entries` (process-local memory). Although `AuditLogger` writes JSONL logs to disk and loads startup history, it does not continuously reconcile with updates from sibling wrapper processes after startup. In multi-process workflows this creates split-brain behavior: charts update with fresh calls while audit/session views remain stale.

## Deliverables
- Implement a shared-source refresh path in `AuditLogger` so API reads can include entries written by sibling processes without requiring process restart.
- Ensure `/api/audit`, `/api/sessions`, and websocket `metrics_update.sessions` read from this same shared source path.
- Add unit/integration regression coverage that simulates multi-process logging by appending to JSONL files and verifies fresh visibility via API routes.
- Document the consistency model and any practical limits in `docs/webui-setup.md` and `docs/troubleshooting.md`.

## Dependencies
- Existing structured JSONL audit log files under configured audit log directory.
- Existing session grouping logic in `src/mcpbridge_wrapper/webui/sessions.py`.
- Existing API surface in `src/mcpbridge_wrapper/webui/server.py` and tests in `tests/unit/webui/`.

## Acceptance Criteria
- [ ] `/api/audit` includes entries written by another process after this process started (without restart).
- [ ] `/api/sessions` is computed from the same refreshed audit entry set used by `/api/audit`.
- [ ] Websocket `metrics_update` payload includes sessions built from that same refreshed source.
- [ ] Regression tests cover cross-process visibility for both audit rows and sessions.
- [ ] Documentation explains shared-source behavior and multi-process expectations.
- [ ] Required quality gates pass: `pytest`, `ruff check src/`, `mypy src/`, `pytest --cov` (coverage >= 90%).

## Validation Plan
1. Add focused tests in `tests/unit/webui/test_audit.py` for on-read history refresh behavior and stale-state recovery.
2. Extend `tests/unit/webui/test_server.py` with an API-level regression asserting `/api/audit` and `/api/sessions` observe externally appended entries from the same log directory.
3. Run full quality gates and record command outputs in `SPECS/INPROGRESS/BUG-T19_Validation_Report.md`.
4. Confirm docs updates describe consistency behavior and mention remaining independent issue tracked in `BUG-T20`.

## Implementation Plan
### Phase 1: Shared audit source reconciliation
- Add a safe refresh mechanism in `AuditLogger` that can reconcile in-memory entries with on-disk JSONL history on read paths.
- Keep ordering and memory cap behavior deterministic (`_max_memory_entries`) while avoiding malformed-line failures.
- Reuse this mechanism for `get_entries`, `get_entry_count`, and export methods so all readers observe the same source.

### Phase 2: Session path alignment with audit source
- Update server session-producing routes (`/api/sessions`, websocket loop) to consume entries from the refreshed audit source path.
- Ensure route-level behavior stays backward-compatible for query params and payload shape.
- Keep BUG-T20 scope separate: if ordering correction is needed for safety, do minimal defensive handling and avoid broad analytics changes.

### Phase 3: Regression tests and docs
- Add explicit regression tests for cross-process visibility drift.
- Update `docs/webui-setup.md` and `docs/troubleshooting.md` with consistency guarantees/limits.
- Capture all quality gate evidence in validation report.

## Notes
- Keep fix scoped to `BUG-T19` (consistency across data sources). Session-duration ordering correctness remains tracked in `BUG-T20` and should only be touched if required for safe operation of this fix.

---
**Archived:** 2026-02-25
**Verdict:** PASS
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Validation Report: BUG-T19

## Task
Audit Log and Session Timeline are inconsistent with tool charts in multi-process runs.

## Implementation Summary
- Added on-read shared-history refresh in `AuditLogger` by tracking JSONL file metadata signatures and reloading when files change.
- Updated all read paths (`get_entries`, `get_entry_count`, `export_json`, `export_csv`) to use refreshed shared history so sibling-process writes are visible without restart.
- Preserved existing memory cap behavior and malformed-line tolerance during refresh.
- Added unit regression coverage in `tests/unit/webui/test_audit.py` for external writer visibility on read paths.
- Added API regression coverage in `tests/unit/webui/test_server.py` verifying `/api/audit` and `/api/sessions` both include sibling-process writes.
- Added integration regression coverage in `tests/integration/webui/test_e2e.py` for multi-process audit/session consistency.
- Updated `docs/webui-setup.md` and `docs/troubleshooting.md` with the multi-process consistency model and operational notes.

## Quality Gates

### 1) `PYTHONPATH=src pytest`
- Result: PASS
- Evidence: `640 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:
- `640 passed, 5 skipped`
- `Required test coverage of 90.0% reached`
- `Total coverage: 91.33%`

## Manual Validation Notes
- Simulated sibling-process logging using a second `AuditLogger` instance bound to the same `audit.log_dir`.
- Confirmed new entry appears via `/api/audit` and is also represented in `/api/sessions` without restarting the web-serving process.
- Session duration/order edge cases remain tracked separately under BUG-T20.

## Verdict
PASS
6 changes: 5 additions & 1 deletion SPECS/ARCHIVE/INDEX.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# mcpbridge-wrapper Tasks Archive

**Last Updated:** 2026-02-25 (BUG-T13_Per-Tool_Latency_Statistics_does_not_show_params_when_capture_params_is_false)
**Last Updated:** 2026-02-25 (BUG-T19_Audit_Log_and_Session_Timeline_are_inconsistent_with_tool_charts_in_multi_process_runs)

## Archived Tasks

| Task ID | Folder | Archived | Verdict |
|---------|--------|----------|---------|
| BUG-T19 | [BUG-T19_Audit_Log_and_Session_Timeline_are_inconsistent_with_tool_charts_in_multi_process_runs/](BUG-T19_Audit_Log_and_Session_Timeline_are_inconsistent_with_tool_charts_in_multi_process_runs/) | 2026-02-25 | PASS |
| BUG-T13 | [BUG-T13_Per-Tool_Latency_Statistics_does_not_show_params_when_capture_params_is_false/](BUG-T13_Per-Tool_Latency_Statistics_does_not_show_params_when_capture_params_is_false/) | 2026-02-25 | PASS |
| BUG-T11 | [BUG-T11_Chart_Request_Timeline_never_shows_actual_events/](BUG-T11_Chart_Request_Timeline_never_shows_actual_events/) | 2026-02-25 | PASS |
| BUG-T12 | [BUG-T12_Audit_Log_does_not_show_new_calls/](BUG-T12_Audit_Log_does_not_show_new_calls/) | 2026-02-20 | PASS |
Expand Down Expand Up @@ -251,6 +252,7 @@
| [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 |
| [REVIEW_bug_t19_audit_session_consistency.md](_Historical/REVIEW_bug_t19_audit_session_consistency.md) | Review report for BUG-T19 |
| [REVIEW_bug_t13_capture_params_hint.md](_Historical/REVIEW_bug_t13_capture_params_hint.md) | Review report for BUG-T13 |
| [REVIEW_bug_t11_request_timeline.md](_Historical/REVIEW_bug_t11_request_timeline.md) | Review report for BUG-T11 |

Expand All @@ -261,6 +263,8 @@

| Date | Task ID | Action |
|------|---------|--------|
| 2026-02-25 | BUG-T19 | Archived REVIEW_bug_t19_audit_session_consistency report |
| 2026-02-25 | BUG-T19 | Archived Audit_Log_and_Session_Timeline_are_inconsistent_with_tool_charts_in_multi_process_runs (PASS) |
| 2026-02-25 | BUG-T13 | Archived REVIEW_bug_t13_capture_params_hint report |
| 2026-02-25 | BUG-T13 | Archived Per-Tool_Latency_Statistics_does_not_show_params_when_capture_params_is_false (PASS) |
| 2026-02-25 | BUG-T11 | Archived REVIEW_bug_t11_request_timeline report |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
## REVIEW REPORT — BUG-T19 Audit/Session Consistency

**Scope:** `origin/main..HEAD`
**Files:** 11

### Summary Verdict
- [x] Approve
- [ ] Approve with comments
- [ ] Request changes
- [ ] Block

### Critical Issues
- None.

### Secondary Issues
- None.

### Architectural Notes
- The task now aligns Audit Log and Session Timeline on the same shared JSONL-backed source by adding read-time history refresh in `AuditLogger`.
- This is intentionally scoped to consistency and does not change session ordering semantics; BUG-T20 remains the tracking item for negative-duration/order correction.

### Tests
- `PYTHONPATH=src pytest` → PASS (`640 passed, 5 skipped`)
- `ruff check src/` → PASS
- `mypy src/` → PASS
- `PYTHONPATH=src pytest --cov` → PASS (`Total coverage: 91.33%`)

### Next Steps
- FOLLOW-UP skipped: no actionable review findings to add as new workplan tasks.
5 changes: 3 additions & 2 deletions SPECS/INPROGRESS/next.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

## Recently Archived

- **BUG-T19** — Audit Log and Session Timeline are inconsistent with tool charts in multi-process runs (2026-02-25, PASS)
- **BUG-T13** — Per-Tool Latency Statistics does not show params when `capture_params` is false (2026-02-25, PASS)
- **BUG-T11** — Chart Request Timeline never shows actual events (2026-02-25, PASS)
- **BUG-T12** — Audit Log does not show new calls (2026-02-20, PASS)

## Suggested Next Tasks

- BUG-T19 — Audit Log and Session Timeline are inconsistent with tool charts in multi-process runs
- BUG-T20 — Session Timeline can show negative duration due to incorrect entry ordering
- BUG-T18 — Error Breakdown widget must be full width streatched
- BUG-T4 — Repeated Xcode permission prompts for each short-lived MCP client process
15 changes: 8 additions & 7 deletions SPECS/Workplan.md
Original file line number Diff line number Diff line change
Expand Up @@ -1636,9 +1636,10 @@ 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
- **Status:** ✅ Fixed (2026-02-25)
- **Priority:** P1
- **Discovered:** 2026-02-20
- **Completed:** 2026-02-25
- **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
Expand All @@ -1663,12 +1664,12 @@ When a different wrapper process receives new events, chart data advances but lo
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
- [x] Reproduce with repeated Cursor reconnects in a multi-process setup and capture API deltas between `/api/metrics`, `/api/audit`, and `/api/sessions`
- [x] Choose and implement a single shared source of truth for audit/session data across processes (SQLite-backed audit store or equivalent)
- [x] Ensure `/api/audit` reflects newly recorded entries regardless of which wrapper process logged them
- [x] Ensure `/api/sessions` is computed from the same shared data source as Audit Log
- [x] Add integration regression test covering reconnect + new initialize row visibility in Audit Log and Session Timeline
- [x] 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
Expand Down
52 changes: 47 additions & 5 deletions Sources/XcodeMCPWrapper/Documentation.docc/Troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,16 +241,58 @@ Both commands show the PID in the second column (`PID`).
**Recovery:**

```bash
# Kill the stale process by PID
kill <PID>
# Kill the listener bound to the port
PORT=8080
PID=$(lsof -tiTCP:$PORT -sTCP:LISTEN | head -n1)
kill "$PID"

# If it survives (some builds do not exit on SIGTERM), force kill
sleep 0.5
ps -p "$PID" >/dev/null 2>&1 && kill -9 "$PID"

# Optionally clear other web-ui wrapper instances (case-insensitive pattern)
pkill -f -i "mcpbridge_wrapper --web-ui" || true

# Or kill all wrapper/bridge processes in one step
pkill -f mcpbridge
# Verify the port is now free (expected: no output)
lsof -nP -iTCP:$PORT -sTCP:LISTEN
```

After stopping the stale process, restart your MCP client (Cursor / Zed / Claude Code) or re-run the `--web-ui-only` command and the port should now be free.
Prefer `kill` (`SIGTERM`) first; use `kill -9` only when the process does not exit.

**Note:** Multiple wrapper processes can run simultaneously on *different* ports. Make sure you identify the PID bound specifically to the port you want, not just any `mcpbridge` process. If the port is immediately re-occupied, close/restart MCP clients (Cursor/Zed/Claude) that may auto-spawn a new wrapper process.

## Error: "Tool charts are fresh, but Audit Log / Session Timeline look stale"

**Symptom:** Chart widgets (request counts, tool distribution) show new activity, but `/api/audit`
and Session Timeline still show older entries.

**Cause:** Multi-process client reconnects can split writes across wrapper processes. Audit/session
views depend on shared JSONL audit files in `audit.log_dir`; if processes are writing to different
log directories or an outdated runtime is still serving UI, views can appear stale.

**Diagnosis:**

```bash
# 1) Verify active dashboard process and port
PORT=8080
lsof -i TCP:$PORT -sTCP:LISTEN

# 2) Check audit log directory configured in that process
curl -s http://127.0.0.1:$PORT/api/config | jq '.audit.log_dir'

# 3) Inspect recent shared audit entries on disk
LOG_DIR=$(curl -s http://127.0.0.1:$PORT/api/config | jq -r '.audit.log_dir')
ls -lt "$LOG_DIR"/audit_*.jsonl | head
tail -n 20 "$LOG_DIR"/audit_*.jsonl 2>/dev/null | tail -n 20
```

**Solution:**
- Ensure all wrapper processes use the same `audit.log_dir` (via shared `--web-ui-config`).
- Restart stale processes so the active dashboard serves current code/config.
- Re-test by issuing a tool call, then refresh `/api/audit` and `/api/sessions`.

**Note:** Multiple wrapper processes can run simultaneously on *different* ports. Make sure you identify the PID bound specifically to the port you want, not just any `mcpbridge` process.
**Note:** Session-duration ordering edge cases are tracked separately in `BUG-T20`.

## Error: "Uptime still shows 1h 0m 0s" or behavior is unchanged after upgrade

Expand Down
16 changes: 15 additions & 1 deletion Sources/XcodeMCPWrapper/Documentation.docc/WebUIDashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ fall back to their defaults.
| `metrics.max_datapoints` | Max data points per series | `3600` |
| `metrics.capture_params` | Record parameter key names per tool call for pattern analysis | `false` |
| `audit.enabled` | Enable audit logging | `true` |
| `audit.log_dir` | Audit log directory | `logs/audit` |
| `audit.log_dir` | Audit log directory (relative paths resolve from the config-file directory; otherwise from current process working directory) | `logs/audit` |
| `audit.max_file_size_mb` | Max log file size | `10.0` |
| `audit.max_files` | Max rotated log files | `10` |
| `audit.capture_payload` | Capture full request/response payloads in the ring buffer | `false` |
Expand Down Expand Up @@ -150,6 +150,20 @@ Table showing Avg / P50 / P95 / P99 / Min / Max latency per tool.
Paginated table of recent tool calls with timestamp, tool name, direction, request ID, latency,
and error message. Supports filter by tool name, JSON export, and CSV export.

### Multi-Process Consistency Model

When multiple wrapper processes write to the same audit log directory (for example, frequent
Cursor reconnects), the dashboard uses this model:

- Audit data is shared through on-disk JSONL files in `audit.log_dir`.
- `/api/audit` refreshes from those files when they change, so entries from sibling processes
become visible without restarting the dashboard process.
- `/api/sessions` is computed from the same refreshed audit entry set used by `/api/audit`.
- Tool charts/KPIs are sourced from `SharedMetricsStore` (SQLite) and remain process-shared.

Known limitation:
- Session ordering/duration edge cases are tracked separately under `BUG-T20`.

## API Endpoints

| Endpoint | Method | Description |
Expand Down
Loading