Files
pad/internal/items
xarmian 25d76af0ba fix(fields): field writes keep every number's literal, so an integer above 2^53 is no longer rounded by a write that never named it (BUG-3202) (#1545)
* fix(fields): field writes keep every number's literal, so an integer above 2^53 is no longer rounded by a write that never named it (BUG-3202)

Every door that writes an item's fields decoded the blob into
map[string]any, turning each JSON number into a float64, so the re-encode
rounded integers above 2^53 (9007199254740993 stored as ...992). The stored
blob was decoded that way too, so a fields_patch to `status` rewrote every
large integer in the row.

- models.DecodeFieldsJSON / DecodeJSONKeepingNumbers decode with UseNumber.
  They accept and refuse exactly what json.Unmarshal does (trailing data,
  out-of-float64-range numbers); only the representation changes.
- Used at every fields decode that feeds a write: create, full-fields
  update, the fields object on the wire, fields_patch (request and store
  merge), the carried-blank-relation normalisation, the Append/Set metadata
  helpers, move, bulk move, cross-workspace copy and its preflight, and the
  move/copy field_overrides (models.FieldValues).
- items.validateFieldType accepts a json.Number that parses as a finite
  number. items.coerceValue and the CLI's parseFieldFlag keep a numeric
  string that is a JSON number literal as that literal.
- Read-only decoders are unchanged (display, hydration, guards).

Claude-Session: https://claude.ai/code/session_01A3rXV7GDLAhBe9mL4V6z2m

* fix(fields): compare field values by value, so an equal number in another spelling is no change and two integers that share a float64 are (BUG-3202)

Field writes now keep a number's literal, so one value can be stored as
different text (1e3 vs 1000). Three places compared field values:

- item.updated's change detector compared the fields blob's TEXT whenever
  it was not masking a string done key: an equal-value rewrite, or a
  key-reorder-only rewrite, emitted item.updated.
- the activity `changes` diff and the BUG-3163 carry check compared
  float64s: equal spellings were fine, but two different integers above
  2^53 compared equal, so such a change went unrecorded and a changed
  reserved value passed as carried.

models.CanonicalJSONNumbers gives every number one exact canonical
spelling. All three decode keeping literals and compare canonical forms.

Claude-Session: https://claude.ai/code/session_01A3rXV7GDLAhBe9mL4V6z2m
2026-09-25 14:22:09 -04:00
..