Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# FU-P11-T2-3: Reorder sessions from the last to the first

**Version:** 1.0.0
**Status:** IN PROGRESS
**Priority:** P2
**Dependencies:** P11-T2 ✅
**Created:** 2026-02-28

---

## Overview

Make the Session Timeline newest-first by returning sessions in descending start-time order.
This ensures the latest activity appears at the top and is labeled as `Session 1`.

---

## Background

The backend session detector currently returns sessions chronologically (oldest-to-newest).
`renderTimeline()` uses the returned array order for labels, so the oldest session is rendered as
`Session 1`. This makes fresh activity harder to find during active debugging.

---

## Deliverables

| File | Change |
|------|--------|
| `src/mcpbridge_wrapper/webui/sessions.py` | Return sessions newest-first while preserving deterministic session IDs and tool ordering inside each session |
| `tests/unit/webui/test_sessions.py` | Update ordering expectations and add explicit newest-first assertions |
| `tests/unit/webui/test_server.py` | Add/adjust API/WebSocket assertions to verify newest-first session ordering |

---

## Acceptance Criteria

- [ ] `GET /api/sessions` returns sessions ordered by latest start time first
- [ ] Timeline labels show the newest group as `Session 1`
- [ ] Refresh and live updates keep the same newest-first ordering
- [ ] Tests cover ordering with at least two sessions at different timestamps
- [ ] `pytest` passes
- [ ] `ruff check src/` passes
- [ ] `mypy src/` passes
- [ ] `pytest --cov` reports ≥ 90% coverage

---

## Implementation Plan

### 1. Update session ordering at source (`sessions.py`)

- Build sessions in chronological order as today (to keep gap grouping logic simple).
- Reverse the completed session list before returning.
- Reindex session IDs after reversal so `session_0` maps to newest session and labels remain intuitive.
- Keep each session's `tools` list in chronological order to preserve intra-session call flow.

### 2. Validate API and WebSocket behavior (`test_server.py`)

- Add endpoint-level assertion that multi-session responses are newest-first.
- Add WebSocket assertion that pushed `sessions` payload is newest-first.

### 3. Validate detector behavior (`test_sessions.py`)

- Update existing multi-session tests to assert newest-first ordering.
- Add explicit test ensuring `session_0` corresponds to newest session.

---

## Risks and Mitigations

- **Risk:** Existing tests or UI logic rely on oldest-first order.
- **Mitigation:** Update tests and keep tool ordering within a session unchanged.

- **Risk:** Session IDs could become unstable after reordering.
- **Mitigation:** Reindex IDs after final ordering so IDs are deterministic and label-safe.

---

## Test Plan

```bash
pytest
ruff check src/
mypy src/
pytest --cov
```

---
**Archived:** 2026-02-28
**Verdict:** PASS
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Validation Report: FU-P11-T2-3

**Task:** Reorder sessions from the last to the first
**Date:** 2026-02-28
**Verdict:** PASS

---

## Changes Made

| File | Change |
|------|--------|
| `src/mcpbridge_wrapper/webui/sessions.py` | Return sessions in newest-first order and reindex session IDs so `session_0` is the newest session |
| `tests/unit/webui/test_sessions.py` | Updated ordering expectations and added explicit newest-first assertions |
| `tests/unit/webui/test_server.py` | Updated API ordering assertions and WebSocket newest-first ordering test |

---

## Acceptance Criteria

- [x] `GET /api/sessions` returns sessions ordered by latest start time first
- [x] Timeline labels show the newest group as `Session 1`
- [x] Refresh and live updates keep the same newest-first ordering
- [x] Tests cover ordering with at least two sessions at different timestamps

---

## Quality Gates

| Gate | Result |
|------|--------|
| `PYTHONPATH=src pytest` | ✅ 661 passed, 5 skipped |
| `ruff check src/` | ✅ All checks passed |
| `mypy src/` | ✅ Success: no issues found in 18 source files |
| `PYTHONPATH=src pytest --cov` | ✅ 91.55% (≥ 90% required) |

---

## New/Updated Tests

`tests/unit/webui/test_sessions.py`
- Updated multi-session expectations to newest-first order
- Added `test_multi_session_output_is_newest_first`
- Extended zero-gap test to assert descending session start order

`tests/unit/webui/test_server.py`
- Updated mixed-order sessions endpoint test to assert newest-first output
- Updated WebSocket test to assert newest-first two-session payload ordering
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-T9_Orphaned_Web_UI_server_process_blocks_port_after_MCP_client_disconnect_or_config_change)
**Last Updated:** 2026-02-28 (FU-P11-T2-3_Reorder_sessions_from_the_last_to_the_first)

## Archived Tasks

| Task ID | Folder | Archived | Verdict |
|---------|--------|----------|---------|
| FU-P11-T2-3 | [FU-P11-T2-3_Reorder_sessions_from_the_last_to_the_first/](FU-P11-T2-3_Reorder_sessions_from_the_last_to_the_first/) | 2026-02-28 | PASS |
| BUG-T18 | [BUG-T18_Error_Breakdown_full_width_layout_fix/](BUG-T18_Error_Breakdown_full_width_layout_fix/) | 2026-02-26 | PASS |
| BUG-T9 | [BUG-T9_Orphaned_Web_UI_server_process_blocks_port_after_MCP_client_disconnect_or_config_change/](BUG-T9_Orphaned_Web_UI_server_process_blocks_port_after_MCP_client_disconnect_or_config_change/) | 2026-02-25 | PASS |
| BUG-T20 | [BUG-T20_Session_Timeline_can_show_negative_duration_due_to_incorrect_entry_ordering/](BUG-T20_Session_Timeline_can_show_negative_duration_due_to_incorrect_entry_ordering/) | 2026-02-25 | PASS |
Expand Down Expand Up @@ -211,6 +212,7 @@
| [REVIEW_P12-T3_error_classification_categorization.md](P12-T3_Add_Error_Classification_and_Categorization/REVIEW_P12-T3_error_classification_categorization.md) | Review report for P12-T3 |
| [REVIEW_P12-T4_data_storage_documentation.md](P12-T4_Add_documentation_about_data_storage/REVIEW_P12-T4_data_storage_documentation.md) | Review report for P12-T4 |
| [REVIEW_P12-T2_param_frequency_analysis.md](P12-T2_Add_Tool_Parameter_Frequency_Analysis/REVIEW_P12-T2_param_frequency_analysis.md) | Review report for P12-T2 |
| [REVIEW_fu_p11_t2_3_session_ordering.md](_Historical/REVIEW_fu_p11_t2_3_session_ordering.md) | Review report for FU-P11-T2-3 |
| [REVIEW_FU-P11-T2-2_limit_query_param.md](_Historical/REVIEW_FU-P11-T2-2_limit_query_param.md) | Review report for FU-P11-T2-2 |
| [REVIEW_FU-P11-T1-1_fake_webuiconfig_refactor.md](_Historical/REVIEW_FU-P11-T1-1_fake_webuiconfig_refactor.md) | Review report for FU-P11-T1-1 |
| [REVIEW_FU-P12-T2-1_stacking_click_listeners.md](_Historical/REVIEW_FU-P12-T2-1_stacking_click_listeners.md) | Review report for FU-P12-T2-1 |
Expand Down Expand Up @@ -269,6 +271,8 @@

| Date | Task ID | Action |
|------|---------|--------|
| 2026-02-28 | FU-P11-T2-3 | Archived REVIEW_fu_p11_t2_3_session_ordering report |
| 2026-02-28 | FU-P11-T2-3 | Archived Reorder_sessions_from_the_last_to_the_first (PASS) |
| 2026-02-26 | BUG-T18 | Archived REVIEW_bug_t18_error_breakdown_layout report |
| 2026-02-26 | BUG-T18 | Archived Error_Breakdown_full_width_layout_fix (PASS) |
| 2026-02-25 | BUG-T9 | Archived REVIEW_bug_t9_orphaned_webui_process report |
Expand Down
31 changes: 31 additions & 0 deletions SPECS/ARCHIVE/_Historical/REVIEW_fu_p11_t2_3_session_ordering.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## REVIEW REPORT — FU-P11-T2-3 Session Ordering

**Scope:** origin/main..HEAD
**Files:** 8

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

### Critical Issues
- None.

### Secondary Issues
- None.

### Architectural Notes
- Session ordering is now enforced at the detector layer (`detect_sessions`), so both REST (`GET /api/sessions`) and WebSocket (`/ws/metrics`) paths inherit the same newest-first behavior.
- Session ID reindexing after reversal keeps timeline labels deterministic (`session_0` == newest session).

### Tests
- Verified by updated unit tests in `tests/unit/webui/test_sessions.py` and `tests/unit/webui/test_server.py`.
- Quality gates executed during EXECUTE:
- `PYTHONPATH=src pytest` → 661 passed, 5 skipped
- `ruff check src/` → pass
- `mypy src/` → pass
- `PYTHONPATH=src pytest --cov` → 91.55% coverage (>= 90%)

### Next Steps
- No actionable findings. FOLLOW-UP step can be skipped.
5 changes: 2 additions & 3 deletions SPECS/INPROGRESS/next.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@

## Recently Archived

- **FU-P11-T2-3** — Reorder sessions from the last to the first (2026-02-28, PASS)
- **BUG-T9** — Orphaned Web UI server process blocks port after MCP client disconnect or config change (2026-02-25, PASS)
- **BUG-T18** — Error Breakdown widget must be full width streatched (2026-02-26, PASS)
- **BUG-T20** — Session Timeline can show negative duration due to incorrect entry ordering (2026-02-25, PASS)

## Suggested Next Tasks

- BUG-T4 — Repeated Xcode permission prompts for each short-lived MCP client process
- BUG-T1 — Kimi CLI MCP Connection Failure
- None (all workplan tasks are complete)
19 changes: 18 additions & 1 deletion SPECS/Workplan.md
Original file line number Diff line number Diff line change
Expand Up @@ -2080,7 +2080,7 @@ Phase 9 Follow-up Backlog

---

#### ⬜️ FU-P11-T2-3: Reorder sessions from the last to the first
#### FU-P11-T2-3: Reorder sessions from the last to the first
- **Description:** Fix session ordering so the Session Timeline shows the most recent session first (newest-to-oldest). Current behavior shows the oldest session first, which makes fresh activity harder to find.
- **Priority:** P2
- **Dependencies:** P11-T2
Expand All @@ -2097,6 +2097,23 @@ Phase 9 Follow-up Backlog

---

#### ⬜️ FU-P11-T2-4: Add one-command Web UI restart workflow
- **Description:** Add a simple restart workflow for developers and users that reliably frees the configured Web UI port and starts a fresh dashboard process after updates.
- **Priority:** P2
- **Dependencies:** P11-T2
- **Parallelizable:** yes
- **Outputs/Artifacts:**
- Updated `src/mcpbridge_wrapper/__main__.py` and/or helper script — support restart semantics (`stop stale listener on port`, then `start`)
- Updated `Makefile` — add a `webui-restart` target
- Updated `docs/troubleshooting.md` and `Sources/XcodeMCPWrapper/Documentation.docc/Troubleshooting.md` — document one-step restart command
- **Acceptance Criteria:**
- [ ] A single documented command restarts Web UI on a chosen port without manual PID hunting
- [ ] Restart flow attempts graceful stop first, then force-kill only if needed
- [ ] Works for both local/dev install and uvx usage
- [ ] Tests cover restart behavior and port-occupied edge case(s) where practical

---

#### ✅ P11-T3: Add Dashboard Theme Toggle (Dark/Light)
- **Description:** Implement CSS-variable-based theme system with a toggle button in the header. Refactor all hardcoded colors in `dashboard.css` to CSS custom properties on `:root`. Add `[data-theme="light"]` overrides. Store user preference in `localStorage`. Update Chart.js color defaults on theme toggle.
- **Priority:** P2
Expand Down
15 changes: 12 additions & 3 deletions src/mcpbridge_wrapper/webui/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@ def detect_sessions(
Must be >= 0. A value of 0 puts each call in its own session.

Returns:
List of session dicts ordered chronologically. Each dict has:
List of session dicts ordered newest-first by session start time.
Each dict has:

.. code-block:: python

{
"id": "session_0", # zero-based index string
"id": "session_0", # zero-based index string (newest session)
"start": 1234567890.0, # timestamp of first tool call
"end": 1234567890.5, # timestamp of last tool call
"tool_count": 3,
Expand Down Expand Up @@ -83,7 +84,7 @@ def detect_sessions(
if current_tools:
sessions.append(_build_session(len(sessions), current_tools))

return sessions
return _newest_first_sessions(sessions)


def _extract_tool(entry: Dict[str, Any]) -> Dict[str, Any]:
Expand All @@ -109,3 +110,11 @@ def _build_session(index: int, tools: List[Dict[str, Any]]) -> Dict[str, Any]:
"error_count": error_count,
"tools": tools,
}


def _newest_first_sessions(sessions: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""Return sessions in newest-first order with deterministic IDs."""
newest_first = list(reversed(sessions))
for index, session in enumerate(newest_first):
session["id"] = f"session_{index}"
return newest_first
32 changes: 18 additions & 14 deletions tests/unit/webui/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def test_sessions_endpoint_sorts_entries_chronologically(self, client, audit, mo
def test_sessions_endpoint_handles_mixed_order_and_preserves_latest_event(
self, client, audit, monkeypatch
):
"""Sessions API yields monotonic boundaries for mixed-order audit rows."""
"""Sessions API yields monotonic boundaries and newest-first session ordering."""
mixed_entries = [
{
"timestamp": 600.0,
Expand Down Expand Up @@ -217,12 +217,14 @@ def test_sessions_endpoint_handles_mixed_order_and_preserves_latest_event(
assert len(sessions) == 2

first, second = sessions
assert first["start"] == 100.0
assert first["end"] == 600.0
assert [t["request_id"] for t in first["tools"]] == ["req-1", "req-2", "req-3"]
assert second["start"] == 1400.0
assert second["end"] == 1400.0
assert second["tools"][-1]["request_id"] == "req-4"
assert first["id"] == "session_0"
assert first["start"] == 1400.0
assert first["end"] == 1400.0
assert first["tools"][-1]["request_id"] == "req-4"
assert second["id"] == "session_1"
assert second["start"] == 100.0
assert second["end"] == 600.0
assert [t["request_id"] for t in second["tools"]] == ["req-1", "req-2", "req-3"]
assert all(session["start"] <= session["end"] for session in sessions)

def test_export_audit_json(self, client, audit):
Expand Down Expand Up @@ -387,14 +389,14 @@ def test_websocket_metrics_update_includes_sessions(self, client, audit):
assert "sessions" in message
assert isinstance(message["sessions"], list)

def test_websocket_sessions_are_sorted_chronologically(self, client, audit, monkeypatch):
"""WebSocket payload session windows use non-decreasing start/end timestamps."""
def test_websocket_sessions_are_newest_first(self, client, audit, monkeypatch):
"""WebSocket payload keeps the newest session at index 0."""
reverse_entries = [
{
"timestamp": 300.0,
"timestamp_iso": "2026-02-25T10:03:00Z",
"timestamp": 600.0,
"timestamp_iso": "2026-02-25T10:10:00Z",
"tool": "LatestTool",
"request_id": "req-3",
"request_id": "req-2",
"direction": "response",
},
{
Expand All @@ -412,8 +414,10 @@ def test_websocket_sessions_are_sorted_chronologically(self, client, audit, monk
message = websocket.receive_json()

sessions = message["sessions"]
assert sessions
assert sessions[0]["start"] <= sessions[0]["end"]
assert len(sessions) == 2
assert [session["id"] for session in sessions] == ["session_0", "session_1"]
assert [session["start"] for session in sessions] == [600.0, 100.0]
assert all(session["start"] <= session["end"] for session in sessions)


class TestAuth:
Expand Down
Loading