Skip to content

PROD-2341: sync single-item linked-content selections correctly - #199

Merged
jules-exel merged 1 commit into
mainfrom
PROD-2341-single-linkedlist
Jul 29, 2026
Merged

PROD-2341: sync single-item linked-content selections correctly#199
jules-exel merged 1 commit into
mainfrom
PROD-2341-single-linkedlist

Conversation

@jules-exel

@jules-exel jules-exel commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

A linked-content dropdown that selects a single item — e.g. Game Banner's "Draw Game" field — arrived empty on the target after a sync, and the component could not be edited. Other components referencing the same list synced fine because they use whole-list links.

Investigated on a live sync 63b1dc5d-us2 (pegasus-qa clone) → 0c45c4b0-us2 (blank). Root cause was three coordinated defects in the content-push path:

1. Push ordering ignored single-item references

The content classifier (getContentItemTypes) only treated whole-list references (fulllist:true) as push-order dependencies. A single-item selection { contentid: N, fulllist: false } was invisible, so the referenced item could be pushed after the item referencing it → reference unresolvable.
Fix: new collectContentIDReferences + classifier promotes single-item contentid/sortids targets into the push-first "linked" bucket.

2. Content references were never remapped

Content-item mapping records expose targetContentID, but the field mapper read .contentID (always undefined) — so contentid/sortids remapping silently no-op'd and the payload shipped with the source id (a dangling reference the target stores as empty).
Fix: resolveTargetContentID() = targetContentID ?? contentID, applied to both the contentid and sortids branches.

3. Object form isn't accepted by the batch engine

The server batch engine reads a linked-content field with string value = row[col] as string (Agility.Shared/Engines/BatchProcessing/BatchProcessing_InsertContentItem.cs). The CLI sent the { contentid, fulllist } object, whose string cast is null → the selection stored empty (rendered as the SharedContent list name, "drawgamesassets").
Fix: emit the scalar remapped contentID string for single-item selections (isSingleItemContentSelection), leaving whole-list links (fulllist:true) and bare nested { contentid } references unchanged.

Verification

Live re-sync 63b1dc5d-us2 → 0c45c4b0-us2 (Game Banner + deps): 28/28 items with a selection populate the correct target id; 0 empty; 2 genuinely had no source selection. Confirmed by reading the field values back from the target via the management API.

Tests

  • content-field-mapper.test.ts — scalar output for single-item selection, targetContentID resolution, sortids remap, and empty / whole-list / bare-reference cases left unchanged.
  • get-content-item-types.test.ts — single-item + sortids targets promoted to linked; empty / unmapped / unknown-id are no-ops; existing whole-list behavior preserved.
  • new collect-content-id-references.test.ts.

🤖 Generated with Claude Code

A linked-content dropdown that selects ONE item (e.g. Game Banner's "Draw Game")
arrived empty on the target after a sync, and the component could not be edited.
Three coordinated defects caused it:

1. Ordering — the content classifier only treated whole-list references
   (fulllist:true) as push-order dependencies. A single-item selection
   ({ contentid: N, fulllist: false }) was invisible, so the referenced item
   could be pushed after the item referencing it, leaving the reference
   unresolvable. Add collect-content-id-references and mark single-item
   contentid/sortids targets into the push-first "linked" bucket.

2. Remap never happened — content-item mapping records expose targetContentID,
   but the field mapper read `.contentID` (always undefined), so content
   references were never remapped and shipped with the SOURCE id (a dangling
   reference the target stores as empty). Fix via resolveTargetContentID()
   (targetContentID ?? contentID) on both the contentid and sortids branches.

3. Serialization — the server batch engine reads a linked-content field with
   `string value = row[col] as string` (Agility.Shared BatchProcessing_
   InsertContentItem.cs); the CLI sent the { contentid, fulllist } object, whose
   string cast is null, so the selection stored empty (rendered as the
   SharedContent list name). Emit the SCALAR remapped contentID string for
   single-item selections (isSingleItemContentSelection), leaving whole-list
   links and bare nested { contentid } references unchanged.

Verified live (63b1dc5d-us2 -> 0c45c4b0-us2): 28/28 Game Banner items with a
selection now populate the correct target id; 0 empty.

Tests: field-mapper (scalar output, targetContentID resolution, sortids,
empty/whole-list/bare-reference untouched), classifier (single-item + sortids
promotion, empty/unmapped no-op), and collect-content-id-references.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jules-exel
jules-exel requested a review from 5PK July 29, 2026 14:13
@jules-exel
jules-exel merged commit bacee90 into main Jul 29, 2026
3 checks passed
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.

2 participants