You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -17,6 +17,7 @@ All notable changes to Maxgram are documented here.
17
17
-**Architecture decision ADR-005** — documents the account migration recovery registry, privacy constraints, remap behavior, and V1 non-automation boundaries.
18
18
19
19
### Changed
20
+
-**PyMax 2.1.2 upgrade** — `maxapi-python` is pinned to 2.1.2. Bridge login validation now accepts tokenless `LOGIN` responses without re-injecting the saved session token, while keeping backend-local sanitizers for unsupported initial-sync payload drift.
20
21
-**Telegram command access model** — `/dm` remains public only in General via an explicit allowlist; `/recovery ...` and other arg commands remain owner-only even in General.
21
22
-**Remap-safe reply routing** — after `/recovery remap`, replies to old Telegram messages no longer send stale MAX `reply_to_msg_id` values when the mapped MAX message belongs to the old chat id.
22
23
-**Recovery snapshot upsert deltas** — `upsert_recovery_snapshot()` now returns `inserted`, `status_changed`, `unmapped`, `needs_invite`, and `manual_admin_required`, and stores a redacted scan reason in recovery events.
@@ -32,6 +33,7 @@ All notable changes to Maxgram are documented here.
32
33
-**Top-level MAX voice payloads** — raw notifications where `payload` itself is the message and media is stored under `attachments` are now normalized before pymax can drop the attachment list.
33
34
34
35
### Tests
36
+
- Added PyMax 2.1.2 runtime version pinning and a regression test for tokenless `LoginResponse` validation.
35
37
- Added coverage for durable inbound/outbound text queues, plaintext clearing after delivery, stale MAX transport readiness, non-queued ambiguous ack timeouts, and non-persisted TG→MAX media failures.
36
38
- Added coverage for DM title resolution order, cached contact name lookup, raw message interceptor, duplicate suppression, top-level raw audio payloads, and recent-history recovery of typed-empty MAX voice events.
37
39
- Added coverage for SQLite recovery migrations/idempotency/deltas, DM contact recovery upsert/export/privacy, recovery report/export/remap, MAX recovery snapshot collection, async event-driven recovery scans, quiet status-summary recovery alerts, account-migration notification privacy/deduplication, owner-only `/recovery`, command allowlist privacy, stale reply routing after remap, and privacy of recovery reports/logs.
- Не делать профилактический/автоматический MAX reauth. Обычный offline/reboot/week-away сценарий должен использовать существующий persisted device token как обычный клиент; reauth разрешён только при явном `requires_reauth=true`/invalid token или осознанной ручной проверке с остановленным bridge. Перед reauth сохранять snapshot `session.db`; не запускать второй MAX-клиент рядом с работающим bridge.
92
92
-`Opcode.SESSIONS_CLOSE` нельзя использовать как точечное закрытие одной старой сессии: live-проверка показала, что вызов с `{"time": ...}` привёл к `FAIL_LOGOUT_ALL` и инвалидировал desktop tokens. Старые MAX sessions закрывать только вручную в телефоне, пока не будет подтверждённого безопасного payload/API.
93
93
- MAX initial sync может вернуть `lastMessage.attaches.type=UNSUPPORTED`; upstream PyMax 2 `LoginResponse` strict union падает. `src/adapters/max/backends/pymax/login.py` ставит backend-local `BridgeAuthService`, который удаляет только unknown attachments до validation.
94
-
- PyMax 2.1.x`LoginResponse` требует `token`, но MAX может не вернуть его при логине с уже существующей session. `BridgeAuthService` в `src/adapters/max/backends/pymax/login.py` перед validation безопасно подставляет текущий `session.token`и логирует только факт repair (`filled_token_from_session=true`), не значение token.
94
+
- PyMax 2.1.2`LoginResponse.token` optional: MAX может не вернуть новый token при логине с уже существующей session, а upstream `App.start()` должен сохранить текущий session token. `BridgeAuthService` больше не подставляет `session.token`в response; он остаётся только для unsupported attachments / non-critical initial-sync payload drift.
95
95
- PyMax 2 может отдать `message.text` как `bytes`; для SHARE/сложных payload это может быть msgpack-like binary, а не plain UTF-8. `MaxEventsService` сначала пробует извлечь `text` через msgpack, затем strict UTF-8, и не форвардит binary garbage с `�`.
96
96
- PyMax 2 TCP msgpack decoder может падать на raw `CHAT_HISTORY`, если MAX отдаёт map с array-like key (`TypeError: unhashable type: 'list'`). `src/adapters/max/backends/pymax/transport.py` ставит backend-local `BridgeMsgpackPayloadCodec`, который конвертирует такие keys в hashable форму до нормализации payload.
97
97
- PyMax 2 TCP `seq` в upstream растёт до `0xFFFFFFFF`, но TCP framer пакует его в one-byte поле; после `255` возникает `struct.error: 'B' format requires 0 <= number <= 255` и ломаются `CHAT_HISTORY`/TG→MAX sends. `src/adapters/max/backends/pymax/transport.py` ставит `BridgeConnectionManager`/sequence guard с wrap `% 0x100`; regression marker: `pymax_tcp_sequence_overflow`.
Copy file name to clipboardExpand all lines: docs/decisions/ADR-004-pymax-reconnect-strategy.md
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
3
3
**Статус:** Принято
4
4
**Дата:** 2026-04
5
-
**Обновлено:** 2026-05-28 для PyMax 2.1.1
5
+
**Обновлено:** 2026-06-04 для PyMax 2.1.2
6
6
**Контекст:** Обнаружен баг при отладке production
7
7
8
8
## Проблема
@@ -53,3 +53,4 @@ async def _make_client(self):
53
53
- Readiness не равен только `_started`: после network/router flap PyMax 2 может закрыть внутренний transport, но не вернуть управление из `start()`. Поэтому `MaxAdapter.is_ready()` проверяет `client.is_connected`; watchdog видит закрытый transport и после grace-period может выполнить self-heal restart процесса.
54
54
- PyMax 2.1.0 исправил TCP header/seq layout (`seq` стал 16-bit). Bridge больше не заворачивает `seq` на 256; `BridgeConnectionManager` оставлен как bridge-owned egress connection boundary с тем же 16-bit range и regression guard.
55
55
- PyMax 2.1.1 исправил upstream TLS `server_hostname` при TCP proxy и сохранение обновлённого session token после login/`close_all_sessions()`. Bridge сохраняет свой `EgressTCPTransport` как MAX-only egress boundary и проверяет `server_hostname` regression-тестом.
56
+
- PyMax 2.1.2 сделал `LoginResponse.token` optional и сохраняет текущий session token, когда `LOGIN` не возвращает новый token. Bridge больше не подставляет token в login response; backend-local `BridgeAuthService` остаётся только для tolerant validation initial-sync payload drift.
Всего: **279 тестов**, async-тесты идут через `pytest-asyncio`, property-based parser guards — через `hypothesis`. Внешних зависимостей нет: SQLite через `tmp_path`, MAX и Telegram заменены stub/fake-классами.
18
+
Всего: **286 тестов**, async-тесты идут через `pytest-asyncio`, property-based parser guards — через `hypothesis`. Внешних зависимостей нет: SQLite через `tmp_path`, MAX и Telegram заменены stub/fake-классами.
19
19
20
20
GitHub Actions выполняет тот же gate: `compileall`, repo-level `ruff check`, scoped bridge `ruff`, scoped `mypy` для MAX/bridge boundaries, затем `pytest --cov=src --cov-report=term-missing --cov-report=xml --cov-report=html --cov-fail-under=75`. HTML/XML coverage отчёты загружаются artifact-ом `coverage-report`.
21
21
22
-
Тесты с marker `architecture` — это service-boundary/refactoring guards (`test_bridge_contracts.py`, `test_max_adapter_leaves.py`, `test_pymax_surface_pin.py`). Их можно отделить от бизнес-регресса командой `pytest -m "not architecture"`; пока они остаются частью полного gate и не отключены.
22
+
Тесты с marker `architecture` — это service-boundary/refactoring guards (`test_bridge_contracts.py`, `test_max_adapter_leaves.py`, `test_pymax_surface_pin.py`). `test_pymax_surface_pin.py` также фиксирует runtime version `pymax.__version__ == "2.1.2"`. Их можно отделить от бизнес-регресса командой `pytest -m "not architecture"`; пока они остаются частью полного gate и не отключены.
23
23
24
24
```text
25
25
pytest -q
@@ -269,6 +269,7 @@ Raw payload implementation is split behind `src/adapters/max/raw_payload.py`: pa
269
269
|`test_max_reauth_snapshot_session_db_copies_without_token_output`| Перед reauth создаётся `session.db.before-reauth-*` snapshot с правами `0600`, без чтения/печати token. |
270
270
|`test_pymax2_login_payload_drops_unsupported_attachments`|`login.py` удаляет unsupported attachments из initial sync payload до strict PyMax 2 `LoginResponse` validation. |
271
271
|`test_pymax2_login_validation_repairs_noncritical_payload_drift`|`validate_login_response()` не валит MAX runtime из-за одного битого `lastMessage`/history/contact узла initial sync. |
|`test_pymax2_login_validation_error_is_safe_and_classified`| Невосстановимый PyMax payload drift логируется без raw payload/`input_val` и классифицируется как `pymax_payload_drift`, reauth не нужен. |
273
274
|`test_pymax2_handler_signatures_are_adapted_to_bridge_callbacks`| PyMax 2 callbacks `(event, client)` адаптируются к bridge callbacks без pymax types снаружи. |
274
275
|`test_pymax2_raw_gateway_converts_frames_and_invokes_app`| Native `on_raw` конвертируется в bridge raw dict, а raw requests изолированы через `_app.invoke`. |
0 commit comments