---
phase: 05-data-surface-stats-bar-detail-panel
plan: "02"
type: execute
wave: 2
depends_on: ["05-01"]
files_modified:
  - possession/tui/screens/main.py
autonomous: false
requirements: [STAT-01, PANEL-01]

must_haves:
  truths:
    - "A stats bar is visible below the Possession top bar, showing Items / Rooms / Containers / Value in a 2-row labeled column layout"
    - "Stats values update automatically whenever items are added, edited, or deleted (after every _load_items() call)"
    - "Pressing Enter on any DataTable row toggles the detail panel — opens if closed, closes if already open"
    - "When detail panel is open and j/k moves cursor, the panel updates live to show the newly highlighted item"
    - "Pressing Escape when the detail panel is open closes the panel and returns focus to DataTable (does not exit app)"
    - "Pressing Escape when no panel/filter/delete-confirm is open exits the app (existing behavior preserved)"
    - "DataTable expands to full width when detail panel is closed (no blank reserved space)"
    - "Detail panel is closed by default on launch"
  artifacts:
    - path: "possession/tui/screens/main.py"
      provides: "MainScreen with StatsBar, DetailPanel, Horizontal body container, updated key/event handlers"
      contains: "StatsBar"
    - path: "possession/tui/screens/main.py"
      provides: "DetailPanel wired to DataTable events"
      contains: "DetailPanel"
  key_links:
    - from: "possession/tui/screens/main.py"
      to: "StatsBar.refresh_stats()"
      via: "_refresh_stats() called at end of _load_items()"
      pattern: "_refresh_stats"
    - from: "possession/tui/screens/main.py"
      to: "DetailPanel.show_item()"
      via: "on_data_table_row_selected() and on_data_table_row_highlighted()"
      pattern: "panel\\.show_item"
    - from: "possession/tui/screens/main.py"
      to: "DetailPanel display toggle"
      via: "on_data_table_row_selected() sets panel.display = not panel.display"
      pattern: "panel\\.display"
    - from: "possession/tui/screens/main.py"
      to: "DataTable (focus restore)"
      via: "self.query_one(DataTable).focus() after panel hide"
      pattern: "query_one\\(DataTable\\)\\.focus"
---

<objective>
Wire StatsBar and DetailPanel (built in Plan 01) into MainScreen. This is a surgical modification of main.py: update compose() layout, extend _load_items() with _refresh_stats(), and implement the three event handlers (RowSelected toggle, RowHighlighted live update, Escape hierarchy extension). Close with a human-verify checkpoint.

Purpose: Deliver the complete Phase 5 user experience — live stats and per-item detail panel — by connecting the new widgets into the existing flat-list screen.
Output: Updated possession/tui/screens/main.py with full Phase 5 behavior.
</objective>

<execution_context>
@/Users/paritoshpatil/.claude/get-shit-done/workflows/execute-plan.md
@/Users/paritoshpatil/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/05-data-surface-stats-bar-detail-panel/05-CONTEXT.md
@.planning/phases/05-data-surface-stats-bar-detail-panel/05-RESEARCH.md
@.planning/phases/05-data-surface-stats-bar-detail-panel/05-01-SUMMARY.md
@possession/tui/screens/main.py
</context>

<tasks>

<task type="auto">
  <name>Task 1: Wire StatsBar + DetailPanel into MainScreen (main.py)</name>
  <files>possession/tui/screens/main.py</files>
  <action>
Surgically update `possession/tui/screens/main.py` to integrate StatsBar and DetailPanel. Read the full current file first, then apply these changes:

**1. Add imports at the top (after existing imports):**
```python
from textual.containers import Horizontal
from possession.tui.widgets.statsbar import StatsBar
from possession.tui.widgets.detailpanel import DetailPanel
```

**2. Update DEFAULT_CSS — add new rules, keep all existing ones:**
```css
/* Add these CSS blocks to DEFAULT_CSS (keep existing rules) */
#main-body {
    height: 1fr;
}
DataTable {
    width: 7fr;
    height: 1fr;
}
#detail-panel {
    width: 3fr;
    height: 1fr;
    border-left: solid $primary-darken-2;
}
```
Remove the standalone `DataTable { height: 1fr; }` rule (now replaced by the scoped rule above with width).

**3. Update compose() — replace the body with:**
```python
def compose(self) -> ComposeResult:
    from possession.tui.widgets.quickadd import QuickAddBar
    yield Static("Possession", id="topbar")
    yield StatsBar(id="stats-bar")
    with Horizontal(id="main-body"):
        yield DataTable(cursor_type="row", show_header=True)
        yield DetailPanel(id="detail-panel", classes="hidden")
    yield Input(
        placeholder="Filter... (/ to open, Esc to clear)",
        id="filter-input",
        classes="hidden",
    )
    yield QuickAddBar(id="quickadd-bar", classes="hidden")
    yield Input(
        placeholder="Delete item? [y/N]",
        id="delete-confirm",
        classes="hidden",
    )
```
Critical notes:
- StatsBar is yielded in NORMAL FLOW (not docked) between topbar and Horizontal — this prevents double-dock overlap with topbar (research pitfall 2)
- DataTable is INSIDE `Horizontal(id="main-body")` — this is what enables 70/30 fractional split
- DetailPanel starts with `classes="hidden"` — closed by default (user decision)
- The `.hidden { display: none; }` rule in DEFAULT_CSS already covers this

**4. Add `_refresh_stats()` method and call it from `_load_items()`:**
```python
def _refresh_stats(self) -> None:
    """Update stats bar with current DB counts."""
    self.query_one(StatsBar).refresh_stats(self.app.db_path)

def _load_items(self) -> None:
    """Load all items and refresh the DataTable and stats bar."""
    table = self.query_one(DataTable)
    try:
        table.clear(columns=True)
    except TypeError:
        table.clear()
    table.add_columns("Name", "Description", "Location", "Category", "Cost")
    self._items = list_items(self.app.db_path)
    self._apply_filter(self._filter_text)
    self._refresh_stats()  # Always refresh stats after loading items
```

**5. Replace `on_data_table_row_selected()` (currently a no-op) with toggle logic:**
```python
def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None:
    """Enter toggles the detail panel open/closed."""
    panel = self.query_one("#detail-panel", DetailPanel)
    panel.display = not panel.display
    if panel.display:
        # Panel just opened — populate with current row
        row_key_str = event.row_key.value if event.row_key else None
        if row_key_str:
            item = next(
                (i for i in self._items if str(i["id"]) == row_key_str),
                None,
            )
            if item:
                panel.show_item(item)
    self.query_one(DataTable).focus()
```

**6. Add `on_data_table_row_highlighted()` handler for live panel update:**
```python
def on_data_table_row_highlighted(
    self, event: DataTable.RowHighlighted
) -> None:
    """Update detail panel live as cursor moves (only when panel is open)."""
    panel = self.query_one("#detail-panel", DetailPanel)
    if not panel.display:
        return
    if not self._items:
        return
    row_key_str = event.row_key.value if event.row_key else None
    if row_key_str is None:
        return
    item = next(
        (i for i in self._items if str(i["id"]) == row_key_str), None
    )
    if item is not None:
        panel.show_item(item)
```

**7. Update `on_key()` — prepend panel escape check BEFORE the existing delete-confirm and filter checks:**
```python
def on_key(self, event: events.Key) -> None:
    """Handle escape hierarchy: panel > delete-confirm > filter > (app exit via binding)."""
    if event.key == "escape":
        # 1. Panel takes priority
        panel = self.query_one("#detail-panel", DetailPanel)
        if panel.display:
            panel.display = False
            self.query_one(DataTable).focus()
            event.prevent_default()
            return
        # 2. Delete confirm (existing)
        del_inp = self.query_one("#delete-confirm", Input)
        if not del_inp.has_class("hidden"):
            del_inp.value = ""
            del_inp.add_class("hidden")
            self._delete_pending_id = None
            self.query_one(DataTable).focus()
            event.prevent_default()
            return
        # 3. Filter input (existing)
        inp = self.query_one("#filter-input", Input)
        if not inp.has_class("hidden"):
            inp.value = ""
            inp.add_class("hidden")
            self.query_one(DataTable).focus()
            self._apply_filter("")
            event.prevent_default()
            return
        # 4. Nothing open -> fall through (action_go_back binding handles exit)
    if event.key == "g":
        if self._last_key == "g":
            self._last_key = ""
            self.action_cursor_top()
            return
        self._last_key = "g"
    else:
        self._last_key = ""
```

**Pitfalls to avoid (from research):**
- Do NOT use `visibility: hidden` — use `display = False` / `classes="hidden"` with `display: none` rule
- Do NOT dock StatsBar — it must be in normal flow
- ALWAYS call `self.query_one(DataTable).focus()` after hiding the panel
- Guard `on_data_table_row_highlighted` with `if not self._items: return` to handle early-mount edge case
  </action>
  <verify>
    <automated>cd /Users/paritoshpatil/Personal/claude-projects/possession-terminal-2 && python -c "from possession.tui.screens.main import MainScreen; from possession.tui.widgets.statsbar import StatsBar; from possession.tui.widgets.detailpanel import DetailPanel; print('PASS: all imports OK')"</automated>
    <manual>Confirm main.py imports cleanly. Check that StatsBar, DetailPanel, Horizontal are all present in the file with correct layout structure.</manual>
  </verify>
  <done>MainScreen imports cleanly; compose() yields StatsBar in flow + Horizontal body with DataTable (7fr) and DetailPanel (3fr, hidden); _load_items() calls _refresh_stats(); on_data_table_row_selected() toggles panel; on_data_table_row_highlighted() does live panel update; on_key() Escape chain checks panel first.</done>
</task>

<task type="checkpoint:human-verify" gate="blocking">
  <name>Task 2: Verify Phase 5 — stats bar and detail panel end-to-end</name>
  <files>N/A</files>
  <action>
Human verification of the complete Phase 5 implementation. Run the app and test each behavior:
- Stats bar visible with 2-row labeled column layout showing Items / Rooms / Containers / Value
- Enter on a row opens detail panel showing all 7 fields (name bold, others with muted labels)
- j/k cursor movement updates panel live when open
- Escape closes panel only (not app), second Escape exits app
- Stats update after adding/editing/deleting an item
  </action>
  <verify>
    <automated>cd /Users/paritoshpatil/Personal/claude-projects/possession-terminal-2 && python -c "from possession.tui.screens.main import MainScreen; print('app loads OK')"</automated>
    <manual>
Run: `python -m possession`

1. Stats bar visible on launch: 2-row labeled columns (Items, Rooms, Containers, Value) between "Possession" header and DataTable.
2. Stats are accurate: item count matches DataTable rows; Value shows $0.00 if no costs set.
3. Enter on any row opens detail panel on the right; DataTable shrinks to ~70% width. Panel shows item name (bold) and all 7 fields.
4. j/k with panel open: panel content updates immediately to the new row.
5. Enter again: panel closes, DataTable expands to full width.
6. Escape with panel open: closes panel, j/k still work. Second Escape exits app.
7. Empty fields display as "FieldName: —"; cost with no value shows "Cost: $0.00".
8. Add an item (press a): Items count in stats bar increments.
9. `python -m possession --transparent`: stats bar and panel render with transparent background.
    </manual>
  </verify>
  <done>All 9 visual checks pass: stats bar visible and accurate, detail panel opens/closes/updates live, Escape hierarchy correct, empty field display correct, stats update on mutation.</done>
</task>

</tasks>

<verification>
End-to-end Phase 5 checks:
1. Stats bar shows all 4 counts correctly — not zero after adding an item
2. Detail panel opens/closes cleanly — DataTable width adjusts (no blank space when panel is closed)
3. j/k navigation with panel open updates panel without lag or errors
4. Escape hierarchy works: panel > delete-confirm > filter > exit (in that priority order)
5. `python -m possession` launches without error (no import errors for new widgets)
</verification>

<success_criteria>
- Stats bar visible below top bar showing live counts (items, rooms, containers, total value)
- Stats update automatically after every add/edit/delete operation
- Enter on a row toggles detail panel; panel shows all 7 fields with correct fallbacks
- j/k cursor movement updates the detail panel live when panel is open
- Escape closes only the panel (not the app) when panel is open
- DataTable fills 100% width when panel is closed (no reserved blank space)
- All existing functionality (filter, quick-add, edit, delete) continues to work unchanged
</success_criteria>

<output>
After completion, create `.planning/phases/05-data-surface-stats-bar-detail-panel/05-02-SUMMARY.md`
</output>
