---
phase: 03-manage
plan: 03
type: execute
wave: 2
depends_on:
  - 03-01
files_modified:
  - possession/tui/screens/main.py
  - possession/tui/screens/edit.py
autonomous: true
requirements:
  - ITEM-01
  - ITEM-02
  - ITEM-03

must_haves:
  truths:
    - "Pressing 'e' on a selected item in items view opens an edit form pre-filled with all current field values"
    - "Saving the edit form updates the item in the database and returns to the items list"
    - "Changing room or container in the edit form moves the item (ITEM-03 satisfied via edit)"
    - "Pressing 'd' on a selected item shows a confirmation prompt"
    - "Confirming deletion removes the item and refreshes the DataTable"
    - "Cancelling deletion (Escape or 'n') returns to the item list unchanged"
  artifacts:
    - path: "possession/tui/screens/edit.py"
      provides: "EditItemScreen with pre-filled form inputs and Save/Cancel actions"
      contains: "class EditItemScreen"
    - path: "possession/tui/screens/main.py"
      provides: "'e' and 'd' key bindings, delete confirmation inline widget, screen push/pop for edit"
      contains: "action_edit_item"
  key_links:
    - from: "possession/tui/screens/main.py"
      to: "possession/tui/screens/edit.py"
      via: "self.app.push_screen(EditItemScreen(item))"
      pattern: "push_screen.*EditItemScreen"
    - from: "possession/tui/screens/edit.py"
      to: "possession/models.py"
      via: "from possession.models import update_item"
      pattern: "update_item"
    - from: "possession/tui/screens/main.py"
      to: "possession/models.py"
      via: "from possession.models import delete_item"
      pattern: "delete_item"
---

<objective>
Implement inline item editing (ITEM-01, ITEM-03) and delete with confirmation (ITEM-02).

Purpose: Users need to correct mistakes and remove items. Edit reuses update_item from Plan 01. Moving an item (ITEM-03) is accomplished by editing the room/container fields — no separate "move" UI needed.
Output: EditItemScreen for the edit form, and delete confirmation inline in MainScreen. Both wired to update_item/delete_item from models.py.
</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/STATE.md
@.planning/ROADMAP.md
@possession/tui/screens/main.py
@possession/tui/app.py
@possession/models.py
@.planning/phases/03-manage/03-01-SUMMARY.md
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create EditItemScreen with pre-filled form</name>
  <files>possession/tui/screens/edit.py</files>
  <action>
Create possession/tui/screens/edit.py with an EditItemScreen class.

**Purpose:** Displays a simple form with one Input widget per field, pre-filled from the item dict passed in. On submit calls update_item(), pops itself off the screen stack, and posts an ItemUpdated message so MainScreen can reload.

**Constructor:**
```python
def __init__(self, item: dict, db_path, **kwargs):
    super().__init__(**kwargs)
    self._item = item
    self._db_path = db_path
```

The `item` dict has these keys from list_items(): id, name, description, room_id, container_id, category_id, room_name, container_name, category_name, purchase_date, cost.

**compose():**
Yield a `Vertical` containing:
- A `Static` label: "Edit Item (Tab to move, Enter to save, Escape to cancel)"
- Input widgets for each editable field (use the string value pre-filled):
  - id="edit-name", value=item["name"] or ""
  - id="edit-description", value=item.get("description") or ""
  - id="edit-room", value=item.get("room_name") or "", placeholder="Room name (leave blank to clear)"
  - id="edit-container", value=item.get("container_name") or "", placeholder="Container name (leave blank to clear)"
  - id="edit-category", value=item.get("category_name") or "", placeholder="Category name (leave blank to clear)"
  - id="edit-purchase-date", value=item.get("purchase_date") or ""
  - id="edit-cost", value=str(item["cost"]) if item.get("cost") is not None else ""

**BINDINGS:**
- ("escape", "cancel", "Cancel")
- ("ctrl+s", "save", "Save")

**action_cancel():** self.app.pop_screen()

**action_save():**
1. Read all Input values.
2. Resolve room name → room_id: if edit-room is non-empty, look up via list_rooms(); if not found, create_room() first (no confirmation needed in edit mode — user explicitly typed the name). If edit-room is empty string, room_id = None (clears location).
3. Similarly resolve container name → container_id using list_containers(room_id=room_id). If edit-container non-empty but room is empty, store container_id=None (can't have container without room).
4. Resolve category name → category_id via list_categories(). If not found, create_category() first. If empty, category_id=None.
5. Parse cost to float if non-empty, else use sentinel _UNSET (import from models) — actually: pass cost=float(val) if val else None. For room_id/container_id/category_id pass the resolved id (which may be None to clear).
6. Call update_item(self._db_path, self._item["id"], name=name_val, description=desc_val, room_id=room_id, container_id=container_id, category_id=category_id, purchase_date=date_val or None, cost=cost_val).
7. self.app.pop_screen()

Note: update_item uses sentinel (_UNSET) to distinguish "don't touch" vs "set to None". Since EditItemScreen always passes all fields explicitly, it always passes real values (including None for cleared fields). The sentinel in update_item handles this correctly because EditItemScreen never omits arguments — it always provides them.

**CSS (inline):** Simple styling — Vertical with padding 1 2, Input with margin-bottom 1.

**Imports:** Path and Optional from stdlib/typing, textual Screen, ComposeResult, Input, Static, Vertical. Models: update_item, list_rooms, list_containers, list_categories, create_room, create_container, create_category.

Python 3.9 compatible type hints throughout.
  </action>
  <verify>
    <automated>cd /Users/paritoshpatil/Personal/claude-projects/possession-terminal-2 && python3 -c "from possession.tui.screens.edit import EditItemScreen; print('OK')"</automated>
  </verify>
  <done>EditItemScreen importable without error</done>
</task>

<task type="auto">
  <name>Task 2: Wire 'e' edit and 'd' delete into MainScreen</name>
  <files>possession/tui/screens/main.py</files>
  <action>
Add 'e' (edit) and 'd' (delete with confirmation) to MainScreen.

**New bindings — add to BINDINGS list:**
```python
("e", "edit_item", "Edit"),
("d", "delete_item", "Delete"),
```

**New state for delete confirmation:**
Add to __init__:
```python
self._delete_pending_id: Optional[int] = None
```

Add to compose() (after filter-input, before or after quickadd-bar):
```python
yield Input(
    placeholder="Delete item? [y/N]",
    id="delete-confirm",
    classes="hidden",
)
```

**action_edit_item():**
1. Only active in items view: if self._view_mode != "items": return
2. Get the currently highlighted row key from the DataTable: `table = self.query_one(DataTable); row_key = table.cursor_row` — use `table.get_row_at(table.cursor_row)` to get displayed data, but we need the actual item dict. Better: find item by row_key. Use `table.cursor_row` index to get the row_key value, then look up item in self._items by id.
3. Get the row_key string: iterate DataTable rows using `table.rows` dict or use `list(table.rows.keys())[table.cursor_row]` to get the RowKey, then `.value` to get the string id.
4. Find item: `item = next((i for i in self._items if str(i["id"]) == row_key_str), None)`
5. If item found: import EditItemScreen inside method, push screen: `self.app.push_screen(EditItemScreen(item, self.app.db_path))`

**on_screen_resume() handler** (called when EditItemScreen pops back to MainScreen):
```python
def on_screen_resume(self) -> None:
    self._load_view()
```
This refreshes the DataTable whenever the screen returns to MainScreen (covers both save and cancel — cancel is harmless re-load).

**action_delete_item():**
1. Only active in items view: if self._view_mode != "items": return
2. Get row_key_str using same technique as edit.
3. Find item in self._items.
4. If found: store self._delete_pending_id = item["id"]; show delete-confirm Input (remove hidden class, set placeholder with item name, focus it).

**on_input_submitted for delete-confirm:**
The existing on_input_submitted handles the filter Input.Changed event. For submitted (Enter pressed), use on_input_submitted:
```python
def on_input_submitted(self, event: Input.Submitted) -> None:
    if event.input.id == "delete-confirm":
        val = event.value.strip().lower()
        event.input.value = ""
        event.input.add_class("hidden")
        self.query_one(DataTable).focus()
        if val == "y" and self._delete_pending_id is not None:
            from possession.models import delete_item
            try:
                delete_item(self.app.db_path, self._delete_pending_id)
            except ValueError:
                pass  # already gone
            self._delete_pending_id = None
            self._load_view()
        else:
            self._delete_pending_id = None
        return
```

Also handle Escape on delete-confirm: in the existing on_key handler, when escape is pressed, check if delete-confirm is visible and hide it if so (before the filter check):
```python
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
```

Add this block BEFORE the existing filter-input escape handling in on_key.

**Row key helper:** To get the current row's key string from the DataTable, use:
```python
row_keys = list(table.rows.keys())
if table.cursor_row < len(row_keys):
    row_key_str = row_keys[table.cursor_row].value
```
  </action>
  <verify>
    <automated>cd /Users/paritoshpatil/Personal/claude-projects/possession-terminal-2 && python3 -c "from possession.tui.screens.main import MainScreen; import inspect; src = inspect.getsource(MainScreen); assert 'action_edit_item' in src; assert 'action_delete_item' in src; print('OK')"</automated>
  </verify>
  <done>'e' and 'd' bindings present, action_edit_item and action_delete_item methods exist, on_screen_resume reloads view, delete confirmation flow is wired</done>
</task>

</tasks>

<verification>
```
cd /Users/paritoshpatil/Personal/claude-projects/possession-terminal-2
python3 -c "from possession.tui.screens.edit import EditItemScreen; print('edit screen OK')"
python3 -c "from possession.tui.screens.main import MainScreen; print('main screen OK')"
python3 -m pytest tests/ -x -q
```
All tests pass. Both screens import without error.
</verification>

<success_criteria>
- EditItemScreen exists in possession/tui/screens/edit.py
- Edit form is pre-filled from item dict; saving calls update_item()
- Changing room/container in edit form moves the item (ITEM-03)
- 'e' key pushes EditItemScreen; on_screen_resume refreshes DataTable
- 'd' key shows delete-confirm Input; 'y' Enter calls delete_item()
- Escape dismisses delete-confirm without deleting
- All existing tests still pass
</success_criteria>

<output>
After completion, create `.planning/phases/03-manage/03-03-SUMMARY.md`
</output>
