## Implementation Plan: Fix Notification Dismissal Marking DMs as Read ### 1. Change: Remove Server-Side Immediate Mark-Read on GET `/messages` **File:** `devplacepy/routers/messages.py` **Line:** 156 **Action:** Delete the line `mark_conversation_read(user["uid"], with_uid)`. **Keep:** The subsequent `mark_notifications_read_by_target()` call on line 157 – it handles notification metadata only. After the change, the relevant section (lines 155–159) will read: ```python messages, other_user = get_conversation_messages(user["uid"], with_uid) # mark_conversation_read(user["uid"], with_uid) ← removed mark_notifications_read_by_target(user["uid"], other_user["uid"]) ``` ### 2. Verify Client-Side `markRead()` WebSocket Behaviour **File:** `static/js/MessagesLayout.js` **Lines:** 40, 117–120, 283–284, 404–407 **No code changes needed.** The existing logic already correctly sends a `{type:"read", with_uid}` message over WebSocket after the page loads (or on receiving a message from the other user). This provides the intended behaviour: DMs are only marked read when the user actually stays on the conversation. ### 3. Ensure No Regressions in Notification Dismissal Path **File:** `templates/notifications.html` (line 35–36) **Action:** No change – the × dismiss button already posts to `POST /notifications/mark-read/{uid}` without redirecting to `/messages`. This path remains correct. ### 4. Add Test Coverage for the Critical Chain **File:** `tests/unit/database.py` (or create a new test in an appropriate test file) **Action:** Add a test that simulates clicking a notification (i.e., calling `GET /notifications/open/{uid}`) and verifies: - The notification is marked as read. - `mark_conversation_read()` is **not** called for the associated DM conversation. Suggested test structure (mark conversation read by checking database state or mocking `mark_conversation_read` to assert it is not invoked). ### 5. Update `Pipfile` or `pyproject.toml` to Allow System Packages (if needed) **File:** `setup.cfg` or a virtual environment configuration **Action:** If the environment shows PEP 668 errors, modify the install command to bypass the restriction. The safest approach is to create a virtual environment inside the workspace and run tests/lint within it. Update the verification commands to: ```bash python3 -m venv .venv && source .venv/bin/activate && pip install -e '.[dev]' -q && make test-unit ``` and similarly for lint. However, to avoid modifying the project’s own verification commands, the agent can manually create a venv before running the commands, or use `--break-system-packages` if allowed in the container. The earlier failures show the plain commands fail due to PEP 668; the agent should ensure a working Python environment. --- ## Definition of Done All of the following must be true: 1. **Code change applied:** Line 156 of `devplacepy/routers/messages.py` is removed (the `mark_conversation_read` call). 2. **All existing unit tests pass** when executed with the project’s test command (`pip install -e '.[dev]' -q && make test-unit`), after ensuring a proper virtual environment is used (e.g. `python3 -m venv .venv && source .venv/bin/activate && ...`) to avoid PEP 668 errors. 3. **Lint passes** with `pip install -q ruff && ruff check .` (also within the venv). 4. **New test added** that verifies `GET /notifications/open/{uid}` does **not** call `mark_conversation_read()`. The test must pass. 5. **Manual verification (optional but recommended):** Simulate the scenario: - Receive a DM notification. - Click the notification to open the conversation. - Navigate away immediately (before WebSocket fires). - Confirm that the conversation still shows as unread in the UI after a refresh. - If the user stays on the conversation for >1 second, the WebSocket marks it read. 6. **No new warnings or errors** introduced by the change in the project’s test or lint output.