105 lines
6.8 KiB
Markdown
105 lines
6.8 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.
|