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