Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0cffe-206f-7323-89d3-b20ca4cf4bf9
134 lines
8.7 KiB
Markdown
134 lines
8.7 KiB
Markdown
# 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.
|