Skip to content

Address edits falsely report changed address details and fail to save #565

Description

@stef-k

User-facing failure

Reported after the backend 1.9.8 release: saving an address at /User/Location/Edit/ returns:

Address details changed while this form was open. Review the latest values and try again.

The address is not saved. Investigate as a high-priority regression in the manual address editing delivered by #560 / PR #562. No concurrent enrichment publication was established in the report.

Evidence and suspected cause

  • Services/LocationManualAddressEdit.cs, HasCurrentProviderTuple, compares the stored ReverseGeocodedAt and submitted OriginalReverseGeocodedAt with exact equality.

  • Areas/User/Views/Location/Edit.cshtml renders OriginalReverseGeocodedAt as a hidden asp-for input without an explicit round-trip format.

  • Suspected: hidden-input formatting loses fractional seconds or changes timestamp representation, causing an unchanged provider publication to be rejected as stale.

  • This is a hypothesis from code inspection, not yet executable confirmation. Verify the actual Razor/InputTagHelper output and MVC-bound value before selecting the fix. Keep genuine concurrent-publication rejection intact.

  • The unfinished diagnostic directly parses the generated value; it does not exercise MVC POST model binding. Its modeled preprocessing is not executable proof of the production InputTagHelper path. Do not describe precision loss as confirmed until the production formatting/binding seam reproduces it.

Preserved local work

  • Repository: C:\Users\stef\source\repos\Wayfarer
  • Branch: fix/location-edit-timestamp-roundtrip
  • Local checkpoint: 71c8b54d432a9df464b722cd0d90ee47099ac0c1
  • Base: 4ec2ee5a887753e42198388e806094c117e0b18e (v1.9.8)
  • Only tests/Wayfarer.Tests/Views/LocationEditViewContractTests.cs changed: an unfinished diagnostic test uses MVC's hidden-input generator with a fractional-second timestamp and reads the format from the production view.
  • The diagnostic currently does not compile: its Location initializer omits required UserId, TimeZoneId, and Coordinates. Consequently, no behavioral red/green result has been obtained.
  • It currently models InputTagHelper preprocessing rather than rendering the complete form. Prefer confirming the actual formatter/model-binding seam without building a large harness.
  • Code Guard and diff check passed. No production source fix, push, PR, release, deployment, provider request, or production data change was made.

Additional reported disclosure defect

Address details lacks the visual disclosure indicator present on Capture Details near the bottom of the Edit view. Its <summary> currently has d-flex, overriding the native list-item display used for disclosure markers. Restore a visible indicator that reflects the actual expanded/collapsed state.

Prefer native <details>/<summary> behavior and native marker rendering, moving layout styling inside the summary if needed. Do not introduce custom JavaScript for disclosure state. Preserve the address preview, precision guidance, and existing automatic expansion for incomplete addresses and invalid submissions.

Bounded implementation and acceptance criteria

Timestamp diagnosis and exact concurrency

  1. Continue from the preserved checkpoint without amending or rewriting it. First repair the diagnostic's required Location initialization.
  2. Establish the smallest executable chain: production hidden-input formatting → emitted form value → MVC-bound AddLocationViewModel value → HasCurrentProviderTuple. Use the actual InputTagHelper with the production field's attributes and metadata, or an existing production-view rendering facility; use the registered MVC model binder and form value provider. Do not substitute handwritten formatting, DateTimeOffset.Parse, or direct model assignment for these seams. No new server/browser harness is required.
  3. Use a nonzero fractional-second timestamp at PostgreSQL-supported precision. Record the original value, emitted value, bound value, binding errors, and comparison outcome using synthetic test data. Confirm or reject the precision-loss hypothesis before choosing the fix.
  4. An unchanged publication must survive rendering and binding exactly under the existing comparison. A genuinely newer publication, including a one-microsecond timestamp change with unchanged provider/storage mode, must remain stale. Preserve exact tuple comparison, owned-row locking, and transaction protection; no tolerance, rounding workaround, or concurrency bypass.
  5. Retain null-timestamp/manual-address behavior. Keep additional formatting cases limited to concrete risks introduced by the selected fix.

Invalid-form redisplay

  1. Preserve submitted editable values, attempted values needed to display binding failures, and validation feedback. Keep Address details expanded and use Wayfarer's custom warning surface.
  2. Keep the original publication tuple associated with the retained submitted address values throughout invalid redisplay. Do not silently refresh that tuple to a newer publication while retaining old address values.
  3. Reload owned server display metadata and activity choices as needed, but keep those concerns separate from the form's original concurrency authority. Ensure model properties and rendered hidden values express the same intended authority.
  4. With no intervening publication, correcting the validation error and resubmitting must save normally.
  5. If a newer publication occurs before invalid redisplay or before corrected resubmission, the retained form must not overwrite it. The corrected stale submission must follow the existing custom conflict-warning and fresh-Edit reload flow, where current address values and their tuple are loaded together.
  6. Clearing all ModelState, or only refreshing/removing hidden-field entries, is not by itself an acceptable fix. Prove the redisplayed values and corrected resubmission using realistic posted ModelState, rather than asserting returned model properties alone.

Save behavior and provenance

  • An ordinary provider-enriched address edit saves and persists the submitted address fields, with existing normalization.
  • Changed addresses clear provider attribution and resolved-feature metadata; unchanged addresses retain provenance. Coordinate changes retain only explicitly changed address fields under the existing rules.
  • Genuine newer publications remain unchanged after stale submissions.
  • Editing, including invalid and stale submissions, makes no provider contact.
  • Preserve existing Wayfarer success, validation, and conflict feedback; do not introduce native browser alerts.

Disclosure indicator

  • Address details has a visible disclosure indicator in both collapsed and expanded states, reflecting the actual details state.
  • Pointer activation and keyboard activation with Enter and Space toggle the disclosure; keyboard focus remains visible.
  • At a narrow viewport, the label and long address preview wrap readably without horizontal overflow, clipping, or obscuring the indicator.
  • Preserve native details/summary semantics and existing automatic-open behavior. Prefer restoring native marker display over adding a custom indicator or JavaScript.

Proportional validation and documentation

  • Extend the existing view-contract diagnostic for actual formatting and binding.
  • Extend the existing invalid-post test for realistic ModelState, rendered authority, and corrected resubmission with unchanged/newer publication outcomes. Avoid duplicating the same matrix across layers.
  • Reuse existing manual-edit provenance/no-contact tests and the existing PostgreSQL stale-publication test. Add only the missing successful persistence evidence at the established relational seam, verified through a fresh context.
  • Perform one bounded browser observation on the production Edit view covering indicator states, pointer/keyboard toggling, and narrow-screen wrapping. Reuse established local test/browser infrastructure and synthetic data; do not duplicate save/concurrency matrices in the browser.
  • Classify unavailable infrastructure separately from product failures and follow repository retry limits.
  • Add an entry under the existing Unreleased changelog section describing the save correction and restored disclosure indicator; document touched code as required by repository instructions.
  • Run focused validation, complete branch-scope Code Guard, and independent review before the normal PR/exact-head CI workflow when implementation is authorized.

Issue #566 is outside scope. This contract review does not authorize implementation or publication; preserve the diagnostic checkpoint unchanged until implementation resumes.

Production log evidence (reviewed 2026-09-05)

The downloaded server log wayfarer-20260904.log records three consecutive Edit POST rejections at 2026-09-04 21:01:29, 21:01:41, and 21:01:59 UTC (2026-09-05 00:01:29, 00:01:41, and 00:01:59 Europe/Athens). Each emits the exact address-change warning and redirects to a freshly loaded Edit form; subsequent saves still fail.

This confirms repeatable server-side conflict rejection despite fresh form loads, rather than merely stale browser content. The log does not provide the submitted and stored concurrency-tuple values, so timestamp precision loss remains suspected, not confirmed. Use the rendered-input/model-binding seam identified above to establish the cause. No raw log, credentials, or location data is attached.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions