frontend-patterns/research/reviews/ZulipAcceptanceProjection.md

118 lines
11 KiB
Markdown
Raw Normal View History

# Zulip message editing — acceptance and projection source study
2026-09-27. LEARN-0016 preregistered at `ff0c1d4`; Codex author-model review.
Pinned Zulip revision `5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c`. Ten source/test
files inspected in selected ranges; [source manifest](ZulipAcceptanceSources.json)
records hashes and exact locators. No application, database, upstream tests or live
inference executed. Test bodies below are evidence of intended checks, not results.
## Result
**Corroboration of the six-question decision aid.** Local editable input, a tentative
display, shared acceptance and subsequent projection remain distinguishable in this
application. Rendering occurs both before acceptance in the browser and during the
server mutation path. A representation is therefore insufficient evidence of shared
acceptance, regardless of where it was computed.
The useful qualification is that the boundaries need not occupy separate objects or
a simple linear UI workflow. A client message object temporarily carries optimistic
content; pending metadata and saved prior content distinguish its meaning. Server
rendering also participates in validation: a rendering failure can prevent acceptance.
Separate architectural questions do not imply independent execution or deployable
components. This is consistent with the aid's existing compatibility qualification.
No new pattern, candidate revision, promotion or independent full-contract use is
claimed. The 0.1 candidate profiles and pinned reference stay unchanged. This is the
same Zulip family as earlier draft/navigation studies, with a newly inspected edit
acceptance path. No new independent family count follows from additional files.
## Source observations and interpretation
ZA labels refer to precise file/range entries in the manifest. These are static
observations. Statements about expected behavior under failure remain hypotheses.
| Claim | Located mechanism | Interpretation and boundary |
| --- | --- | --- |
| ZA01 — local editing | `message_edit.ts` stores the active textarea and its pre-edit raw content separately; save reads both. | Proposed content and its base are distinct from shared acceptance. These maps do not establish restart persistence. |
| ZA02 — tentative projection | For eligible content, save records original values, marks a local edit timestamp, invokes local echo and closes the edit form before PATCH completion. Backend-only syntax bypasses this branch. `echo.ts` renders Markdown and rerenders lists. | The message object can contain tentative display state. Browser projection is conditional; it is not authority over shared content. A pending notice is described in code; its actual appearance/accessibility was not tested. |
| ZA03 — shared validation | The endpoint calls `check_update_message`, then returns success. The action uses a transaction and locked message access, checks edit permission/time and optional prior-content hash, normalizes input and renders it before applying changes. `access_message` selects the message row for update. | Located locking and hash comparison support a content precondition under this path. The hash is optional at the API, covers content rather than all message state, and is not an operation identity or monotonic revision. No concurrent database execution or all-writer audit performed. |
| ZA04 — persistence and notification | The direct-message branch calls the save helper and schedules an update event; the channel branch also saves and arranges cache invalidation after commit. `send_event_on_commit` registers a transaction callback. Events include raw and rendered content. | Server-side change and subsequent notification are separate steps. The save helper and queue transport were not followed beyond this cap; an on-commit callback is not proof of durable eventual delivery, replay or receipt retention. |
| ZA05 — accepted representation | `message_events.update_messages` replaces cached rendered content, clears pending display metadata, ends the edit UI, updates raw content and rerenders lists. HTTP success separately clears echo bookkeeping; it does not return the full displayed message. | HTTP response and event application are distinct evidence paths. Their ordering was not established. Event-driven replacement need not be a new fetch or client recomputation of Markdown. |
| ZA06 — error recovery | The edit error callback, when the original list is current, restores saved display values and can reopen proposed text. A mismatch code receives a specific message. The channel wrapper forwards ordinary errors, with reload/authentication exceptions. | The located callback does not demonstrate an unknown-outcome protocol or an operation lookup. Restoring a display is not proof that a server transaction rolled back. Lost-response/event races and navigation-away behavior require a bounded test before defect or safety claims. |
| ZA07 — upstream test intent | `test_edit_message_race_condition` issues two sequential edits with the original content hash; it expects the second rejected and the first retained. Frontend tests cover eligibility and limits. | The backend test models a stale client; its name does not establish overlapping transactions. Neither inspected test range exercises the response/event race or durable recovery. No upstream test was run. |
## Six questions applied to one content edit
1. **Decision scope and acceptor.** The user controls an in-page proposal. The server
path checks permission and the supplied content base before changing the shared
message. Client edit eligibility helps interaction but does not replace those checks.
This says nothing about every state scope in Zulip.
2. **Acknowledgement.** Typing changes input; echo makes proposed content visible;
HTTP success supplies a response from the update endpoint; the update event supplies
server-rendered content. Visibility alone is not acceptance. Server commit followed
by a lost response remains untested, as does event arrival before the HTTP callback.
3. **View input and retention.** Before submission, the base is the raw content saved
when editing begins. During echo the displayed object contains locally rendered
proposed content with pending metadata. After the event, it contains the server's
representation. These are transient client observations here, not an established
local primary record with a crash recovery contract.
4. **Computation placement.** The edit form and eligible optimistic Markdown are
computed in the browser. Server Markdown is computed in the acceptance path and
delivered in an event; the browser then updates its lists. Initial page bootstrap
and offline reconstruction were outside the selected path. No SSR/client-only
classification of the whole application follows from it.
5. **Delivery and failure.** The edit response and event are separate deliveries;
neither is an inspected streaming-HTML contract. Backend-only syntax waits for a
different projection path. Explicit mismatch feedback and generic errors exist,
but ordering, lost events, retry identity and accessible announcements remain unknown.
6. **Invalidating constraint.** Requiring final shared acceptance while disconnected,
persistent local editing after a crash, safe replay of an unknown operation or
preservation of another in-progress edit would need evidence beyond these paths.
A responsive optimistic display does not satisfy those stronger requirements.
## Contrasts that prevent overgeneralization
- Local echo deliberately modifies the displayed message model before acceptance.
Distinguish state meaning and pending status; do not require a separate object for
every architectural state as though it were part of the pattern contract.
- Content containing backend-only syntax does not take the optimistic branch. The
same authority scope can use different projection schedules depending on content.
- Rendering can fail inside the transaction before the update is applied. Projection
and acceptance are separate questions with dependencies, not freely interchangeable
stages. Source code uses a durable transaction decorator; no crash/flush guarantee
was measured and the decorator name must not be treated as such evidence.
- The prior-content hash prevents a mismatch only when supplied and only for its
content scope. Equal content after intervening edits can have the same hash. Do not
substitute it for complete history identity, an edit-operation receipt or replay
deduplication. This is a contract distinction, not a discovered upstream defect.
- Event application calls `end_message_edit` by message ID. Source alone does not
establish that any currently open input belongs to that event's edit attempt.
The eligibility helper blocks editing while echo bookkeeping is pending; that
narrows possible schedules. Reopening after the HTTP success callback, or an event
from another client, remains a relevant follow-up, not a reproduced loss claim. Likewise, restoring original display data after an error is not a
verified reconciliation policy for an already committed but unacknowledged edit.
## Candidate implications and residual ownership
CAND-0005 gains a located example of temporary base/proposal separation, but event
ownership and restart retention are not established. CAND-0006 has a concrete partial
content-check/lock path; optional scope and unexecuted concurrency prevent declaring
full-contract recurrence. CAND-0007's distinct outcome requirement remains important;
this edit path does not establish its proposed identity/lookup contract. CAND-0008
must allow server-provided replacement as well as refetching, already compatible with
its contract. These are comparisons, not evidence-profile upgrades in this seed study.
One corroboration outcome answers the registered question. No case becomes validated,
no ontology gate closes, and this sixth prospective comparison group creates no
comparable trend window. Manifest retrieval history retains the initial web-tool cache
miss; shell raw-source retrieval subsequently succeeded for all ten files.
WP-0003 T01 retains a bounded response/event ownership question: check an acknowledged
edit event against a newly opened edit, an event before the HTTP callback, a lost
response after acceptance, and navigation away before failure. Reuse the pinned
application/test infrastructure if feasible; do not restart the paused navigation
fixture or claim a defect before reproducing it. Stop if transport setup exceeds a
new preregistered budget. WP-0003 T04 retains actual storage/receiver fault validation;
this study supplies neither a local durability test nor an operation replay guarantee.
The older Zulip auxiliary-navigation residual and WP-0002 human/usefulness gate stay open.