--- 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)_