Skip to content

fix(metadata): close the DATE-array storage gap left by #275 - #278

Merged
dmealing merged 2 commits into
mainfrom
fix/date-array-storage-and-effective-type
Aug 8, 2026
Merged

fix(metadata): close the DATE-array storage gap left by #275#278
dmealing merged 2 commits into
mainfrom
fix/date-array-storage-and-effective-type

Conversation

@dmealing

@dmealing dmealing commented Aug 8, 2026

Copy link
Copy Markdown
Member

Closes the carry-forward the #275 batch deliberately left unfixed. That batch fixed the serializer's write side for @isArray fields but stopped at its bounded-scope clause, which named these exact files as scope-creep triggers.

Two root defects

  1. MetaField.setObject converted via the field's SCALAR type. getDataType() on an @isArray field ran a List through a scalar converter — comma-joining a STRING array, bracketed-toString() for the rest — and setObjectAttribute then rejected the corrupted scalar. So setBoolean/setInt/setLong/setDouble/setStringArray were all broken for array fields, and MetaObjectDeserializer's own array-read branches threw. Now uses getEffectiveDataType(), which is provably a no-op for every non-array field.
  2. DataConverter had no DATE_ARRAY implementation (case DATE_ARRAY:unsupported(), with a commented-out call showing it was anticipated and never built). Adds toDateArray, mirroring its sibling converters exactly.

BYTE_ARRAY/SHORT_ARRAY deliberately stay unsupported — field.byte/field.short were cut from the metamodel as non-functional stubs.

Also closed

The two _blockedByPreexistingSetterBug pins become real round-trips, plus five findings deferred only because this gap made them untestable: the per-element hard cast, field.timestamp array coverage, the real-Gson-pipeline exercise, null-array breadth, and the DATE-array read path.

Review caught a genuine asymmetry: the serializer emits JsonNull at a null element position — pinned by its own test — while the deserializer threw reading it back. Fixed with a guard placed before the isJsonPrimitive check (JsonNull is not a primitive), proven by a full-pipeline round-trip.

Also pins a deliberate widening this change introduces: an array-typed field receiving a scalar value previously threw and now converts (comma-splitting "a,b"["a","b"]). That converges setObject with the primary storage path, which already converted against the effective type.

Verification

metadata 1347/1347 · omdb,om 63/63 · two independent reviews, one fix round, re-reviewed clean.

dmealing and others added 2 commits August 8, 2026 17:55
Two root defects in the JSON array-write storage path, deliberately left
unfixed by the #275 batch as out-of-scope scope-creep triggers:

- MetaField.setObject(Object,Object) converted via the field's SCALAR
  getDataType() instead of the array-aware getEffectiveDataType(), so an
  isArray field's List value was corrupted (comma-joined / bracketed
  toString()) before setObjectAttribute's own instanceof check rejected it.
  Broke setBoolean/setInt/setLong/setDouble/setStringArray, and every
  MetaObjectDeserializer array-read branch that routes through it.
- DataConverter had no DATE_ARRAY case (the commented-out
  `//toDateArray(val)` fragment), so no entry point could store a
  List<Date> on an isArray field.date/field.timestamp field.

Fix: getEffectiveDataType() in MetaField.setObject (a strict no-op for
every non-array field); a new DataConverter.toDateArray mirroring the
sibling toLongArray/toBooleanArray shape, wired into case DATE_ARRAY.
BYTE_ARRAY/SHORT_ARRAY stay on the unsupported arm (field.byte/field.short
are non-functional stubs).

Also closes the deferred findings this unblocked: MetaObjectSerializer's
DATE-array element loop now converts via DataConverter.toDate(o) instead
of a hard cast; field.timestamp (incl. @localTime) array coverage; a
full-Gson-pipeline round trip for the previously-untestable DATE-array
read path in MetaObjectDeserializer; null-array-itself pins extended to
every touched type; and a stale MetaObjectDeserializer comment describing
the DATE-array branch as blocked, now rewritten to match reality.

Blast-radius checked: every MetaField.setObject caller either passes a
scalar value (no-op under the fix) or already routed around the array bug
via setObjectArray/setValue; no caller depended on the corrupting
conversion succeeding.

Refs #275

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015TqsuDye2SfXGf43vuoD3n
…ld widening (#275)

Review fix round 1 on the #275 DATE-array storage gap fix.

Important #1: MetaObjectDeserializer's readDateElement fell through to
el.getAsString() for a JsonNull element (JsonNull is not a JsonPrimitive),
which throws UnsupportedOperationException naming no field -- the same
bare-throw shape C1 eliminated on the write side. Reachable by design:
MetaObjectSerializer deliberately emits JsonNull.INSTANCE at a null
element position (pinned by an existing test), so the serializer's own
output could not be read back by its sibling deserializer. Fixed with an
isJsonNull() guard as the first line of readDateElement, matching what
write already emits; added a full-pipeline null-element round-trip test;
corrected the readFieldValue DATE-case comment that (falsely) claimed the
branch already round-tripped end to end.

Important #2: the report's blast-radius argument conflated "no-op for a
non-array field" with "no-op for every setObject call site" --
getEffectiveDataType() == getDataType() is a property of the field, not
the value. An array-typed field receiving a scalar JSON value (live in
MetaObjectDeserializer's own else-arms) genuinely changed from a loud
InvalidValueException to a silent single-element wrap (or, for STRING, a
comma-split) -- verified both directions by temporarily reverting the E2
fix and confirming the old exception. Not a new rule: it converges with
DataObjectBase._setObjectAttribute's pre-existing effective-type
conversion, which is what produced this task's own E1 RED evidence in the
first place. Added six pinning tests and corrected the report's claim in
place.

Refs #275

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015TqsuDye2SfXGf43vuoD3n
@dmealing
dmealing merged commit 6ba3b78 into main Aug 8, 2026
1 check passed
@dmealing
dmealing deleted the fix/date-array-storage-and-effective-type branch August 8, 2026 22:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant