monal-rebuild/UX-REPLY-RESULTS.md

122 lines
7.6 KiB
Markdown

# Quoted replies — 2026-10-08
The operator approved package 8 and rollout after package 7. Sol High is
retained; no delegation. Existing blunix handoff edits (177 lines) remain
unstaged. Baseline release is generation 555, client `f6feaa7`.
## Representation decision
XEP-0461 0.2.1 selects origin-id/message-id for direct messages and the
room-issued stanza-id for group messages. MAM cursor IDs are separate.
Replies carry a sender-scoped navigation hint; lookup never crosses a
conversation/account, and omitted-sender ID collisions are unresolved.
Fallback quotes use XEP-0428 codepoint counts, not UTF-16 string lengths.
Stored quote snapshots survive missing/edited history without pretending
to identify a different original. An existing edit can be quoted with the
original ID; navigation displays its current saved version.
Legacy OMEMO still encrypts a UTF-8 string, now including the quoted prefix
and answer. There is no new cryptographic payload/namespace. The outer body
is content-independent. Reply ID/address and fallback range stay outside
encryption; reply linkage and quote length are visible metadata. Quote text
is never put there. The web client applies ranges after decryption only;
for encrypted messages only an actual quoted prefix may be separated.
Metadata is not authenticated and is used only as a navigation hint.
Quoted encrypted originals fail closed if encryption is unavailable.
The operator reports **Monal Stable 6.4.21**. Public Monal source uses plain
`> ` text quotes and displays decrypted text as the body. Structured reply
support is still an [open upstream feature](https://github.com/monal-im/Monal/issues/1671).
Exact release mapping: Stable merge **`9be038e9`**, subject `6.4.21 (#1650)`,
also tag `Build_iOS_1092`. Its `chatViewController.m` Quote action prefixes
each selected line with `> ` and adds an empty line; `MLMessageProcessor.m`
uses decrypted text as body without reply/fallback handling. Both exact
6.4.21 files were inspected, after checking develop and oldstable/6.4-line.
Source evidence is not physical-client acceptance.
Sources: [reply IDs/fallback](https://xmpp.org/extensions/xep-0461.html),
[fallback ranges](https://xmpp.org/extensions/xep-0428.html),
[Monal 6.4.21 release](https://github.com/monal-im/Monal/commit/9be038e9268cc7c6aa57be0a3958cafddfe8e39f),
[Monal 6.4.21 text quotes](https://github.com/monal-im/Monal/blob/9be038e9268cc7c6aa57be0a3958cafddfe8e39f/Monal/Classes/chatViewController.m),
[Monal 6.4.21 decrypted body](https://github.com/monal-im/Monal/blob/9be038e9268cc7c6aa57be0a3958cafddfe8e39f/Monal/Classes/MLMessageProcessor.m).
## Implementation and checks
Explicit Reply, composer preview/Cancel/Escape, draft snapshot persistence,
focus return, same-chat jump/highlight, local missing-history status and
44px controls are implemented. Group reflections add their room stanza ID
to an existing local echo. No database migration/dependency/server change.
Checks: zero svelte-check errors/warnings, **74 passing tests** and
production build. Tests exercise Unicode fallback, bad ranges/senders,
group IDs vs archive IDs, ambiguous/cross-chat lookup, encrypted deferred
parsing, draft restart/cancel/send races and real Double Ratchet/AES-GCM
round trip of the complete quote. Wire XML contains neither original nor
answer text; the legacy decoder receives the expected quoted text.
The first candidate exposed a real Svelte-proxy/IndexedDB clone failure on
reply-draft restart. Draft persistence and send capture now take a plain
state snapshot; the restart regression passes in both engines.
Candidate Chromium `state-chromium-1791411343829` and WebKit
`state-webkit-1791411434234` pass `QUOTED-REPLIES`: actual encrypted A/B
send, zero quote/answer text in wire XML, Unicode, restart/settings/chat
isolation/cancel focus, failed send retained, carbon/archive reload without
duplication, focused original navigation, missing local history, an original
outside the latest 60-message page, room-stamped group references and
44px/no-overflow layouts at 320/402/740/1280. Zero page errors.
WebKit reading anchors/pagination/late sizing and draft-send races also pass.
Its additional phone-menu check initially ran before roster-supplied actions
were ready; the runner now waits for the contact Call control before checking
menu order. Focused WebKit `state-webkit-1791411540489` passes all phone
controls (menu/modal focus, themes, safe-area layouts and large text), zero
page errors. Production source did not change for that fixture correction.
Chromium `state-chromium-1791411605534` passes phone controls, reading anchors,
draft-send races, actual closed-profile offline history/draft recovery and
Retry (47 ms first socket, 292 ms online), zero page errors. Linux WebKit's
fully offline cold-start limitation remains; actual offline coverage uses
Chromium. The physical iPhone screen background remains operator-confirmed
from package 7, not a new simulation claim.
Final candidate JS `assets/index-DlzpKHNm.js`, SHA-256
`44b6735197c887591b15a7a87c7b1b196872f73347a6cec4ef5b7b5d8ad7a335`.
A separate candidate run sent the operator one original and one quoted
answer from the existing audit account, both verified as legacy encrypted
stanzas with no original/answer text in outer XML. The operator explicitly
confirmed on **Monal Stable 6.4.21**: “Ja, beides einmal lesbar” for the
quoted original and the answer. This is actual encrypted PWA → Monal
display acceptance; Monal's own text-only replies remain the documented
readable fallback without structured original navigation. No personal-
account/device changes.
## Release and live evidence
Client **`8ea43b9`** is pushed to main, pinned by the isolated blunix
**`084f7fa`** lock commit pushed to capability-layout. Switch succeeded as
**generation 556**, system
`/nix/store/q06xgjsnkc4k1r0y9xx3cib0v4dwv3nk-nixos-system-blunix-26.11.20260910.8ce4ef6`;
nginx root `/nix/store/8r40d45r1g73cr1mv55xa9jsglybslbx-0ff-chat-0.1.0`.
Candidate, store and HTTPS JS hashes match. Worker is unchanged:
`e28aaebf01c98e3252e29968a67f1bc8896518e465682f7dc54b3b2f06e85a06`.
HTTPS 200, revalidation/Vary and unchanged strict CSP verified; iPhone
translucent-status metadata is retained. Only nginx restarted; all ten
checked platform services are active, zero failed units. Rollback: **555**,
preserving phone/background and reconnect fixes. Existing 177 blunix handoff
additions remain unstaged. The local preview is stopped.
Live Chromium `state-chromium-1791412183448` passes matching HTTPS
bundle/CSP and the full quoted-reply flow (encrypted direct, group, drafts,
original/missing/older-history navigation and phone layouts), zero page
errors. Retry opens a socket in 48 ms and is online in 204 ms. The first live
run's expiry-only query-error fixture was healed by asynchronous freshness
updates, so no send error occurred. The final fixture uses an explicit
empty list for a known encrypted peer, fails closed, and restores its exact
saved cache row before recovery; real ratchets/keys are untouched.
Live WebKit `state-webkit-1791412474302` passes matching HTTPS bundle/CSP,
phone controls and the full quoted-reply flow, zero page errors. A preceding
run passed all assertions but recorded `Context is stopped`; its historical
report is retained. The final repeat did not reproduce it; error source and
stack capture were added to the runner without filtering errors or changing
the deployed client. All 19 owned audit rooms are confirmed absent; local
seed fixtures were removed and the preview remains stopped. Actual
Monal acceptance is recorded above; VoiceOver/keyboard/touch ergonomics and
fully offline Linux WebKit remain separate checks/limitations. Package 9
still requires its own encrypted-attachment interoperability design.