frontend-patterns/research/reviews/ActivationConflictsAndReload.md
tegwick f273a190f0 Test activation conflicts and draft-preserving application reload
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0cffe-206f-7323-89d3-b20ca4cf4bf9
2026-09-26 23:30:15 +02:00

84 lines
5.5 KiB
Markdown

# Activation conflicts and unfinished-work reload — 2026-09-26
Status: executed synthetic HTTP/browser experiment, editorial analysis by Codex.
WP-0003 T01, continuation of [feature release lifecycle](FeatureReleaseExperiment.md).
[Runner](../experiments/feature-release/conflicts-reload.mjs),
[reload host](../experiments/feature-release/reload-host.js) and
[receipt](../experiments/feature-release/conflicts-reload-results.json) record 21/21
expected observations: three HTTP update cases plus nine reload cases in Chromium
151.0.7922.34 and Firefox 155.0. No live deployment, backend domain write or model call.
## Activation-update cases
Two clients read the same revision before either submits. The runner then forces a
known write ordering over real loopback HTTP; this is a reproducible stale-writer
interleaving, not an uncontrolled concurrency load test.
| Case | Expected and observed |
| --- | --- |
| C01 Blind whole-map replacement | Reports update succeeds, then a stale Account replacement silently restores the old Reports binding: negative lost-update control |
| C02 Conditional replacement, disjoint intent | Stale Account replacement rejected; explicit refetch and reapplication of only Account intent preserves both updates |
| C03 Competing intent for one feature | Stale Reports rollback rejected; active newer Reports binding retained; no automatic overwrite |
The fixture uses an integer revision carried in If-Match and a synthetic 409 conflict
response, not a full HTTP ETag/precondition implementation. Comparison and replacement
have no asynchronous gap in one server process. A distributed store still needs its
own atomic conditional-write primitive. Refetch alone does not justify retry: C02
reapplies a disjoint intent; C03 deliberately leaves conflicting intent unresolved.
These tests do not execute import-map-deployer or verify its implementation.
## Reload cases
The user-controlled update path offers keep-working, preserve-and-reload, and explicit
discard-and-reload. Persistence uses sessionStorage with synthetic non-sensitive text,
a draft schema and a base-record marker. No server save or remote acceptance is inferred.
| Case | Expected and observed in both browsers |
| --- | --- |
| R01 Unguarded reload | Unsaved input disappears and R2 loads: negative control |
| R02 Defer update | Draft stays editable in the old R1 session |
| R03 Preserve and reload | R2 loads with exact draft text and original base marker recovered |
| R04 Inject storage refusal | Reload blocked; original draft and R1 remain |
| R05 Incompatible target draft schema | Update deferred before reload; original input retained |
| R06 Explicit discard | Both current input and an older stored draft cleared; R2 starts empty |
| R07 Unknown write outcome | Reload blocked while the synthetic pending-outcome flag is set |
| R08 Edit during asynchronous preflight | Latest edited text, not an earlier snapshot, persists and returns |
| R09 Activation changes after preflight snapshot | New document detects schema mismatch again; stored text retained in read-only recovery state |
R08/R09 hold a server response at an explicit barrier, make the intervening edit or
activation change, then release it. No arbitrary settling delay or automatic retry
is used. The poll waits for the barrier itself. R09 demonstrates why a successful
preflight cannot promise that the next document has the same compatibility contract.
The fixture deliberately changes its declared draft schema; it does not implement
an actual schema migration or prove compatibility of a real feature's draft format.
## Limits and interpretation
“Pass” includes intentionally observed failures in C01 and R01. The preservation
path is an authored policy, not an automatic browser or framework guarantee. Explicit
recovery retains bytes; it does not resolve a stale base, merge edits or establish
permission to retain/export them. The unknown-write flag tests a UI gate only; no
real operation lookup or idempotent replay occurs. Discard does not reverse a remote
operation and is blocked by that same unknown-outcome gate.
sessionStorage covers this tab's same-origin reload, not device loss, guaranteed
browser restart or independent backup. The injected write refusal tests the failure
branch, not browser quota exhaustion. Other tabs, login/access revocation, storage
schema migration, interruptions during navigation, native reload/tab close and browser
crashes are not covered. An application-level update button is not a universal unload
interceptor. Persisted drafts remain until explicit discard in this fixture; a real
product needs an authorized retention/cleanup policy and safe handling of stale drafts.
## Candidate disposition
[Independent Feature Boundary](../candidates/IndependentFeatureBoundary.md) gains
conditional activation and session-transition obligations. [Local Draft State](../candidates/LocalDraftState.md)
gains a partial implementation of explicit preservation/refusal, with post-navigation
schema revalidation. [VC-06](../walkthroughs/VC06-MicrofrontendEstate.md) remains
provisional: this supports its release/reload trace, not production rollback or a
completed accessibility/security review. Corpus remains seventeen hypotheses.
Next WP-0003 T01 evidence: navigation/history/full-versus-fragment caching, which is
still pending from VC-03. Retain distributed activation atomicity, authorized draft
retention and native-unload/crash behavior as limits requiring evidence before a
production recommendation; do not grow this fixture into a release platform.