frontend-patterns/research/walkthroughs/VC01-TransactionalAdministration.md
tegwick 78bd951568 Exercise Matrix SDK recovery under controlled transport failures
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0cffe-206f-7323-89d3-b20ca4cf4bf9
2026-09-25 11:02:03 +02:00

134 lines
8.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# VC-01 — Transactional administration
Status: synthetic draft, self-reviewed by Codex 2026-09-25. Baseline repository:
`cfa249a`. No application runtime or empirical usability result. Evidence references
T01–T05 resolve in [the source review](../reviews/TransactionalEvidenceExpansion.md).
## Fixed brief and explicit assumptions
Shared records, server-validated writes, concurrent edits, deep links, keyboard
operation and recoverable failed requests. Example: edit a shared approval record.
Only server acceptance can change its authoritative approval status.
Additional design assumptions, not properties proven by the sources: each read has
an opaque version; the server atomically checks version, authorization and business
rules with the write. Each submitted attempt has an operation ID scoped to principal
and record, bound to its payload, with a durable result lookup and deduplication
retention covering the agreed recovery period. A modified proposal gets a new ID.
These backend capabilities must be verified before implementation. T03 motivates
bounded retry identity but does not establish this whole protocol.
Drafts survive failed requests while the page remains open. Restart survival is not
required by this brief; adding it requires a separate persistence/privacy decision.
Record identity appears in the deep link; version and draft are separate state.
Authorization is checked on every write and on result access. Sensitive draft data
is not placed in URLs. No numerical latency target is assumed.
## Baseline without pattern labels
Load a record and its version. Keep edits apart from received data. Submit the edits
with the version they were based on. Keep them if validation fails. If someone else
changed the record, show the current record beside the proposal and ask the user to
review it. If the response disappears, investigate that operation before sending a
new one. Refresh affected views after acceptance without overwriting newer typing.
## Ordered decisions and resulting contexts
1. Establish server authority over approved records. Local typing gives feedback but
cannot approve a record. Offline acceptance is outside this case.
2. Use [Local Draft State](../candidates/LocalDraftState.md) to preserve user intent
independently of refreshed observations. This creates a base/draft/current comparison.
3. Use [Version-Checked Mutation](../candidates/VersionCheckedMutation.md) for the
conditional write (T01). Blind overwrite risks lost edits; GitLab's error ambiguity
(T02) demonstrates why adapters must preserve actual endpoint semantics.
4. Use [Mutation Outcome Tracking](../candidates/MutationOutcomeTracking.md) to keep
transport state separate from acceptance. A timeout is unknown, not rejection.
Retry only under the operation contract; a disabled button alone cannot deduplicate.
5. Use [Invalidate and Reproject](../candidates/InvalidateAndReproject.md) to refresh
affected observations after acceptance. Keep drafts intact and reject obsolete
responses. T04 shows why browser cancellation cannot enforce server ordering.
6. Defer optimistic approval display. Immediate input and pending feedback suffice;
T05's optimistic example does not justify claiming approval before validation.
## Failure and recovery trace
| Step | Observed state and action | Required invariant |
| --- | --- | --- |
| 1 | A and B read record R at v10; A types draft dA | Typing changes neither server record nor approval |
| 2 | B's valid conditional command commits v11 | Server atomically validates and advances version |
| 3 | A submits operation x against v10; explicit stale-version rejection arrives | dA remains; mark x rejected, not accepted |
| 4 | Read v11; user compares base v10, dA and v11, then proposes dA2 | No blind automatic resend that erases B's intent |
| 5 | Operation y against v11 fails a business rule before commit | Preserve dA2; explain the actual field/domain error |
| 6 | User corrects it; new operation z succeeds at v12 but response is lost | Mark z unknown; do not show approval as confirmed |
| 7 | Lookup z or replay the identical z within the deduplication contract | Resolve its stored result without a second transition |
| 8 | Accepted z identifies v12; refresh detail and relevant list | Acceptance and freshness remain separate facts |
| 9 | A late read of v11 arrives after v12; user has also started another draft | Discard obsolete observation; preserve new draft |
Opaque versions are compared for equality or through a documented server ordering
contract, never sorted numerically by a generic client. The v10/v11/v12 labels above
are explanatory sequence labels. If another actor changes the record after z, a
read alone cannot establish whether z committed: use operation-specific evidence.
If retention expires before z can be resolved, stop automatic resubmission and show
an unresolved outcome with a reconciliation path. Never infer rejection from a 500,
timeout or browser cancellation alone. If refreshed data cannot be obtained, show
accepted-with-stale-view rather than converting acceptance into a failed mutation.
Quality acceptance proposals: keyboard access to comparison, correction and retry;
text status that does not rely on color; an announced outcome and an error summary
linked to fields; stable focus when a refresh arrives; no silent draft replacement.
Revoked access yields an authorization outcome and prevents further submissions;
retention/export of sensitive drafts follows the product's explicit access policy.
These are requirements for later implementation review, not accessibility certification.
## Change exactly one constraint
Replace concurrent editors with a single authorized writer; all other requirements
remain. The competing-editor branch is omitted. Server validation, draft retention,
unknown-outcome recovery and refresh remain necessary. Multiple in-flight attempts
from that one writer can still race, so version checks are not automatically removed.
This branch weakens one motivation for version checking without erasing transport
uncertainty or turning the application into a local-authoritative editor.
## Self-review
| Criterion | Assessment | Reason |
| --- | --- | --- |
| Traceability | pass for reasoning | Each source claim is separated from proposed backend/UI contracts |
| Coherence | revise | Operation lookup, retention and atomic validation still need implementation evidence |
| Decision usefulness | unknown | Labels organize obligations; baseline reaches the same design; no independent comparison |
| Quality coverage | revise | Keyboard/privacy criteria specified but not exercised |
| Changed constraint | pass for reasoning | One writer removes cross-user conflict while preserving other failures |
Next: inspect real frontend recovery implementations and review this case alongside
[VC-02](VC02-OfflineFieldNotes.md). No canonical admission or M1 completion follows
from completing a synthetic narrative.
## Recovery lifecycle clarification — 2026-09-25
Retain the submitted payload in an operation record before clearing or reusing the
composer. This is a logical handoff in this page-session design, not a durability
claim. R01–R05 in the [implementation review](../reviews/FrontendRecoveryImplementations.md)
show why recoverable intent need not stay in the original input box.
For step 9, tag reads with a local request generation. Confirming z advances the
generation and invalidates outstanding reads issued before that acceptance. Such an
old response cannot replace the current observation. A post-acceptance read must
meet a documented read-after-write consistency contract (or provide a comparable
server revision); request generation alone cannot repair stale replicas. Unequal
opaque versions do not reveal which is newer. Without that server contract, show
freshness as unresolved. Preserve newer typing independently of read generations.
Correlate every acknowledgement, server event and error to its original operation.
Once authoritative evidence confirms z, a late transport error cannot regress it to
unknown; a receipt for z cannot clear a new draft or mark a later operation accepted.
A status label such as failed_request is not itself rejection evidence.
## Executable supporting observation — 2026-09-25
[Matrix recovery experiment](../reviews/MatrixRecoveryExperiment.md) X03 exercises
one related client mechanism: an early server event can reconcile an operation even
when its request promise later rejects. In this case, derive z's outcome from
correlated authoritative evidence rather than treating any rejected promise as a
failed approval. A user cancelling local tracking cannot undo z; reversal requires
a separate authorized domain action after its outcome is known. The SDK experiment
does not implement this approval application or its server transaction contract.