surfaces/.planning/phases/01-matrix-qa-polish/01-VERIFICATION.md
Mikhail Putilovskij 6ced154124 feat(matrix): land QA follow-ups and refresh docs
- harden Matrix onboarding/chat lifecycle after manual QA
- refresh README and Matrix docs to match current behavior
- add local ignores for runtime artifacts and include current planning/report docs

Closes #7
Closes #9
Closes #14
2026-04-05 19:08:58 +03:00

138 lines
13 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
phase: 01-matrix-qa-polish
verified: 2026-04-03T09:39:38Z
status: human_needed
score: 24/24 must-haves verified
re_verification:
previous_status: gaps_found
previous_score: 19/24
gaps_closed:
- "!yes reads pending_confirm from store and returns action description"
- "build_skills_text no longer mentions reactions 1-9"
- "!settings returns a read-only dashboard with skills/soul/safety/chats status"
- "No Matrix tests rely on hardcoded legacy C1 assumptions from the old DM flow"
gaps_remaining: []
regressions: []
human_verification:
- test: "Matrix client Space UX"
expected: "First invite creates a visible Space with Chat 1, !new creates a child room under that Space, and !archive / !yes / !no feel correct in a real Matrix client."
why_human: "Element or another Matrix client must render Space membership, room hierarchy, and invite UX; this cannot be proven from repository-only checks."
---
# Phase 1: Matrix QA & Polish Verification Report
**Phase Goal:** Переработать Matrix адаптер с DM-first на Space+rooms, убрать реакции в пользу !yes/!no, довести до уровня "приемлемо работает" как Telegram.
**Verified:** 2026-04-03T09:39:38Z
**Status:** human_needed
**Re-verification:** Yes — after gap closure
## Goal Achievement
### Observable Truths
| # | Truth | Status | Evidence |
| --- | --- | --- | --- |
| 1 | Bot creates a Space on first invite | ✓ VERIFIED | `handle_invite` creates a private Space with `space=True` in `adapter/matrix/handlers/auth.py:37`. |
| 2 | Bot creates first chat room inside that Space | ✓ VERIFIED | `handle_invite` creates `Чат 1`, links it via `m.space.child`, and stores room metadata in `adapter/matrix/handlers/auth.py:51`. |
| 3 | Bot invites user to both Space and chat room | ✓ VERIFIED | `client.room_invite(space_id, ...)` and `client.room_invite(chat_room_id, ...)` in `adapter/matrix/handlers/auth.py:72`. |
| 4 | `space_id` is stored in `user_meta` | ✓ VERIFIED | `user_meta["space_id"] = space_id` in `adapter/matrix/handlers/auth.py:77`. |
| 5 | Repeated invite is idempotent | ✓ VERIFIED | Existing `user_meta.space_id` short-circuits invite flow in `adapter/matrix/handlers/auth.py:22`; covered by `tests/adapter/matrix/test_invite_space.py:54`. |
| 6 | Initial chat id comes from `next_chat_id` | ✓ VERIFIED | `chat_id = await next_chat_id(...)` in `adapter/matrix/handlers/auth.py:75`; dynamic progression asserted in `tests/adapter/matrix/test_invite_space.py:66`. |
| 7 | `!new` creates a room and links it into the user's Space | ✓ VERIFIED | `make_handle_new_chat` calls `room_create`, `room_put_state`, and `room_invite` in `adapter/matrix/handlers/chat.py`; covered by `tests/adapter/matrix/test_chat_space.py:25`. |
| 8 | `!new` without `space_id` returns a user-facing error | ✓ VERIFIED | Handler returns `"Ошибка: Space не найден..."` in `adapter/matrix/handlers/chat.py:39`; covered by `tests/adapter/matrix/test_chat_space.py:52`. |
| 9 | `!archive` archives chat state without Space-child removal | ✓ VERIFIED | `make_handle_archive` delegates only to `chat_mgr.archive` in `adapter/matrix/handlers/chat.py:119`; covered by `tests/adapter/matrix/test_chat_space.py:76`. |
| 10 | `!rename` updates Matrix room name when client is available | ✓ VERIFIED | `client.room_set_name(ctx.surface_ref, new_name)` in `adapter/matrix/handlers/chat.py:106`. |
| 11 | `RoomCreateError` from `!new` is handled gracefully | ✓ VERIFIED | User-facing `"Не удалось создать комнату."` in `adapter/matrix/handlers/chat.py:66`; covered by `tests/adapter/matrix/test_chat_space.py:97`. |
| 12 | Outgoing UI sends plain text with `!yes / !no`, no reactions | ✓ VERIFIED | `send_outgoing` emits only `m.room.message` and appends the command hint in `adapter/matrix/bot.py:140`; covered by `tests/adapter/matrix/test_send_outgoing.py:18`. |
| 13 | `_button_action_to_reaction` is removed | ✓ VERIFIED | No such symbol exists in `adapter/matrix/bot.py`; reaction path is absent. |
| 14 | `on_reaction` callback is removed | ✓ VERIFIED | `MatrixBot` registers only message and member callbacks in `adapter/matrix/bot.py:200`. |
| 15 | `ReactionEvent` import is removed | ✓ VERIFIED | `adapter/matrix/bot.py` imports no reaction event types. |
| 16 | `build_skills_text` no longer mentions reactions `1-9` | ✓ VERIFIED | `build_skills_text` renders only command help in `adapter/matrix/reactions.py:6`; enforced by `tests/adapter/matrix/test_reactions.py:10`. |
| 17 | `build_confirmation_text` uses `!yes/!no` | ✓ VERIFIED | `build_confirmation_text` returns the command-only prompt in `adapter/matrix/reactions.py:16`. |
| 18 | `!yes` resolves pending confirmation | ✓ VERIFIED | `make_handle_confirm` reads `(event.user_id, payload.room_id)` in `adapter/matrix/handlers/confirm.py:14`; adapter round-trip covered by `tests/adapter/matrix/test_send_outgoing.py:63` and a fresh inline spot-check returned `Подтверждено: Archive room`. |
| 19 | `!no` clears pending confirmation | ✓ VERIFIED | `make_handle_cancel` clears the same scoped key in `adapter/matrix/handlers/confirm.py:41`; covered by `tests/adapter/matrix/test_send_outgoing.py:112` and a fresh inline spot-check returned `Действие отменено.` |
| 20 | `!settings` is a read-only dashboard | ✓ VERIFIED | Dashboard output in `adapter/matrix/handlers/settings.py:48` contains snapshot sections only; `tests/adapter/matrix/test_dispatcher.py:161` and a fresh spot-check confirm `Изменить` is absent. |
| 21 | Previously broken Matrix tests are green | ✓ VERIFIED | `pytest tests/adapter/matrix/ -q` passed with `39 passed in 0.75s`. |
| 22 | MAT-01..MAT-12 tests exist and are green | ✓ VERIFIED | Dedicated invite/chat/send_outgoing/confirm coverage exists in `tests/adapter/matrix/` and passed in the Matrix suite. |
| 23 | Full test suite exceeds 96 passing tests | ✓ VERIFIED | `pytest tests/ -q` passed with `112 passed in 3.48s`. |
| 24 | No Matrix tests rely on hardcoded legacy `C1` assumptions from the old DM flow | ✓ VERIFIED | Room-aware regressions now assert dynamic chat allocation and room-id separation in `tests/adapter/matrix/test_invite_space.py:66`, `tests/adapter/matrix/test_dispatcher.py:54`, and `tests/adapter/matrix/test_send_outgoing.py:63`. Remaining `C1` literals are generic sample chat ids, not DM-flow assumptions. |
**Score:** 24/24 truths verified
### Required Artifacts
| Artifact | Expected | Status | Details |
| --- | --- | --- | --- |
| `adapter/matrix/store.py` | pending-confirm helpers and metadata helpers | ✓ VERIFIED | Composite pending-confirm keys exist and are used by bot and confirm handlers. |
| `adapter/matrix/handlers/auth.py` | Space+rooms invite flow | ✓ VERIFIED | Creates Space, links `Чат 1`, stores metadata, invites the user, and sends welcome text. |
| `adapter/matrix/room_router.py` | room-aware chat resolution without auto-registration | ✓ VERIFIED | Returns stored `chat_id` or explicit `unregistered:{room_id}` fallback. |
| `adapter/matrix/handlers/chat.py` | Space-aware `!new`, `!archive`, `!rename` | ✓ VERIFIED | Wired via handler registration and covered by chat-space tests. |
| `adapter/matrix/bot.py` | reaction-free send path and pending-confirm persistence | ✓ VERIFIED | `OutgoingUI` persists confirmations under `(matrix_user_id, room_id)` before `!yes/!no` resolution. |
| `adapter/matrix/converter.py` | command-only Matrix callback conversion | ✓ VERIFIED | `!yes` and `!no` carry `room_id`; no `from_reaction` export remains. |
| `adapter/matrix/reactions.py` | command-only helper text | ✓ VERIFIED | Skill and confirmation text mention commands, not reactions. |
| `adapter/matrix/handlers/confirm.py` | `!yes/!no` handlers using pending confirmations | ✓ VERIFIED | Runtime and legacy fallback paths both behave correctly. |
| `adapter/matrix/handlers/settings.py` | read-only `!settings` dashboard | ✓ VERIFIED | Snapshot-only dashboard is wired and tested. |
| `tests/adapter/matrix/test_invite_space.py` | invite-flow regression coverage | ✓ VERIFIED | Covers Space creation, idempotency, and non-hardcoded chat allocation. |
| `tests/adapter/matrix/test_chat_space.py` | Space-aware chat command coverage | ✓ VERIFIED | Covers `!new`, missing `space_id`, archive, and `RoomCreateError`. |
| `tests/adapter/matrix/test_send_outgoing.py` | outgoing UI and confirm round-trip coverage | ✓ VERIFIED | Covers send path, no reactions, and scoped confirm/cancel round trips. |
| `tests/adapter/matrix/test_confirm.py` | confirm handler coverage | ✓ VERIFIED | Covers scoped confirmation, cancel, no-pending behavior, and legacy fallback. |
### Key Link Verification
| From | To | Via | Status | Details |
| --- | --- | --- | --- | --- |
| `adapter/matrix/handlers/auth.py` | `adapter/matrix/store.py` | `set_user_meta(...space_id...)` | ✓ WIRED | `space_id` is persisted immediately after invite flow. |
| `adapter/matrix/handlers/auth.py` | `adapter/matrix/store.py` | `next_chat_id` | ✓ WIRED | Initial chat ids are allocated dynamically, not hardcoded. |
| `adapter/matrix/handlers/chat.py` | `adapter/matrix/store.py` | `get_user_meta` for `space_id` | ✓ WIRED | `!new` refuses to proceed without stored Space metadata. |
| `adapter/matrix/handlers/chat.py` | Matrix API | `m.space.child` | ✓ WIRED | New rooms are linked into the user Space with `room_put_state`. |
| `adapter/matrix/bot.py` | `adapter/matrix/store.py` | `set_pending_confirm(store, matrix_user_id, room_id, ...)` | ✓ WIRED | Confirm state is stored under runtime Matrix identity. |
| `adapter/matrix/handlers/confirm.py` | `adapter/matrix/store.py` | `get_pending_confirm` / `clear_pending_confirm` | ✓ WIRED | Confirm handlers resolve and clear the same scoped key as the sender path. |
| `adapter/matrix/converter.py` | `adapter/matrix/handlers/confirm.py` | callback payload carries `room_id` | ✓ WIRED | `!yes/!no` callbacks preserve room context across dispatch. |
### Data-Flow Trace (Level 4)
| Artifact | Data Variable | Source | Produces Real Data | Status |
| --- | --- | --- | --- | --- |
| `adapter/matrix/handlers/auth.py` | `space_id`, `chat_id` | `client.room_create(...)`, `next_chat_id(...)` | Yes | ✓ FLOWING |
| `adapter/matrix/handlers/chat.py` | `space_id` | `get_user_meta(store, event.user_id)` | Yes | ✓ FLOWING |
| `adapter/matrix/bot.py` + `adapter/matrix/handlers/confirm.py` | pending confirmation | `set_pending_confirm(store, matrix_user_id, room_id, ...)` -> `get_pending_confirm(store, event.user_id, room_id)` | Yes | ✓ FLOWING |
| `adapter/matrix/handlers/settings.py` | dashboard sections | `settings_mgr.get(...)`, `chat_mgr.list_active(...)` | Yes | ✓ FLOWING |
### Behavioral Spot-Checks
| Behavior | Command | Result | Status |
| --- | --- | --- | --- |
| Matrix-only tests | `pytest tests/adapter/matrix/ -q` | `39 passed in 0.75s` | ✓ PASS |
| Full test suite | `pytest tests/ -q` | `112 passed in 3.48s` | ✓ PASS |
| Real `send_outgoing` -> `!yes` path | inline Python spot-check | Returned `Подтверждено: Archive room`; pending entry cleared | ✓ PASS |
| Real `send_outgoing` -> `!no` path | inline Python spot-check | Returned `Действие отменено.`; pending entry cleared | ✓ PASS |
| `!settings` output | inline Python spot-check | Snapshot dashboard rendered; `Изменить` absent | ✓ PASS |
### Requirements Coverage
| Requirement | Source Plan | Description | Status | Evidence |
| --- | --- | --- | --- | --- |
| none | 01-01..01-06 | No explicit `requirements:` IDs declared in phase plans or roadmap | ✓ N/A | Verification performed against previous must-haves, locked decisions from `01-CONTEXT.md`, and current codebase behavior. |
### Anti-Patterns Found
| File | Line | Pattern | Severity | Impact |
| --- | --- | --- | --- | --- |
| none | - | No blocker or warning-level stub patterns detected in the phase artifacts re-checked for gap closure. | Info | Remaining `C1` literals are benign sample values in tests, not evidence of DM-first wiring. |
### Human Verification Required
### 1. Matrix Client Space UX
**Test:** Invite the bot from a real Matrix account, accept the Space and room invites, run `!new`, then exercise a confirmation flow that requires `!yes` and `!no`.
**Expected:** The Space should appear in the client sidebar, new rooms should appear as Space children, and confirmations should resolve cleanly without falling back to `Нет ожидающих подтверждений.`
**Why human:** Repository checks cannot validate Element or other Matrix-client rendering, invite visibility, or perceived UX quality.
### Gaps Summary
Automated re-verification closed all four previously reported gaps. Phase 01 now satisfies the code-level must-haves and locked decisions: Space+rooms invite flow is wired, reaction UX is removed, `!yes/!no` works end-to-end on scoped pending state, `!settings` is snapshot-only, and the full test suite is green at 112 tests. The only remaining work is manual client-side verification of Matrix UX.
---
_Verified: 2026-04-03T09:39:38Z_
_Verifier: Claude (gsd-verifier)_