Skip to content

fix(agent-client): serialize fields[] projection as comma-separated string (PRD-437)#1622

Merged
christophebrun-forest merged 2 commits into
feat/prd-214-server-step-mapperfrom
fix/prd-437-agent-client-fields-projection-csv
Jun 5, 2026
Merged

fix(agent-client): serialize fields[] projection as comma-separated string (PRD-437)#1622
christophebrun-forest merged 2 commits into
feat/prd-214-server-step-mapperfrom
fix/prd-437-agent-client-fields-projection-csv

Conversation

@christophebrun-forest
Copy link
Copy Markdown
Member

@christophebrun-forest christophebrun-forest commented Jun 3, 2026

Problem

A getData / read-record step run through the workflow-executor against a Ruby agent returns only the last requested field. Reproduced: reading first_name, last_name, email returns only email. Works in browser mode; breaks only in Orchestrator mode (through the executor). TS agents are unaffected.

Root cause

QuerySerializer.formatFields returns an array for the sparse fieldset, e.g. { 'fields[Customer]': ['first_name','last_name','email'] }. superagent serializes arrays via qs.stringify(obj, { indices: false }) as repeated keys without brackets:

fields[Customer]=first_name&fields[Customer]=last_name&fields[Customer]=email

Rack-based (Ruby) agents collapse duplicate scalar keys to the last value, so parse_projection (fields.split(','), expecting the JSON:API comma-separated string) only ever sees the last field → only email + the PK come back.

  • Browser OK: the frontend already sends fields[Customer]=first_name,last_name,email.
  • TS agent OK by luck: it does fields.toString().split(','), and Array.toString() rejoins with commas. Ruby does fields.split(',') → no tolerance for an array.

Fix

formatFields now joins each projection into a single comma-separated string (fields[T]=a,b,c), the JSON:API sparse-fieldset standard, parsed correctly by both Ruby and Node agents. Also fixes the same class of issue for update-record / getRelatedData, which share this serialization.

Sibling of PRD-273 #8 (filter operator casing). Distinct from PRD-432 (JWT casing).

Test plan

  • agent-client: 322/322 tests pass, including a new dedicated regression test asserting fields[users] is a comma-separated string, not an array.
  • Round-trip verified: fields[Customer]=first_name,last_name,email"...".split(",") = ["first_name","last_name","email"].

fixes PRD-437

🤖 Generated with Claude Code

Note

Serialize fields[] query params as comma-separated strings in agent-client

Ruby's Rack middleware collapses repeated query params, so fields[users]=id&fields[users]=name is not handled correctly. QuerySerializer.formatFields in query-serializer.ts now joins projection arrays into a single comma-separated string (e.g. fields[users]=id,name). Tests in both query-serializer.test.ts and server.test.ts are updated to assert string values instead of arrays.

Changes since #1622 opened

  • Removed ticket reference from inline comment in QuerySerializer.serialize test [ff9c180]

Macroscope summarized f73aeb3.

@linear-code
Copy link
Copy Markdown

linear-code Bot commented Jun 3, 2026

PRD-437

@qltysh
Copy link
Copy Markdown

qltysh Bot commented Jun 3, 2026

1 new issue

Tool Category Rule Count
qlty Structure Function with many returns (count = 4): formatFields 1

@christophebrun-forest christophebrun-forest force-pushed the fix/prd-437-agent-client-fields-projection-csv branch from d7b155e to b12a18c Compare June 3, 2026 09:12
@qltysh
Copy link
Copy Markdown

qltysh Bot commented Jun 3, 2026

Qlty


Coverage Impact

Unable to calculate total coverage change because base branch coverage was not found.

Modified Files with Diff Coverage (1)

RatingFile% DiffUncovered Line #s
New Coverage rating: A
packages/agent-client/src/query-serializer.ts100.0%
Total100.0%
🚦 See full report on Qlty Cloud »

🛟 Help
  • Diff Coverage: Coverage for added or modified lines of code (excludes deleted files). Learn more.

  • Total Coverage: Coverage for the whole repository, calculated as the sum of all File Coverage. Learn more.

  • File Coverage: Covered Lines divided by Covered Lines plus Missed Lines. (Excludes non-executable lines including blank lines and comments.)

    • Indirect Changes: Changes to File Coverage for files that were not modified in this PR. Learn more.

…tring (PRD-437)

QuerySerializer.formatFields returned an array for the sparse fieldset
projection (e.g. { 'fields[Customer]': ['first_name','last_name','email'] }).
superagent serializes arrays via qs.stringify(obj, { indices: false }) as
repeated keys without brackets:

  fields[Customer]=first_name&fields[Customer]=last_name&fields[Customer]=email

Rack-based (Ruby) agents collapse duplicate scalar keys to the LAST value, so
parse_projection (which does fields.split(',')) only ever saw the last field.
A getData/read-record reading first_name, last_name, email returned only email
(plus the primary key). The TS agent was unaffected by luck: it does
fields.toString().split(','), and Array.toString() rejoins with commas.

Join each projection into a single comma-separated value (JSON:API sparse
fieldset standard), which both Ruby (split) and Node agents parse correctly.
Also fixes the same class of issue for update-record and getRelatedData, which
share this serialization. Sibling of PRD-273 #8 (filter operator casing).

Update the mcp-server consumer tests that asserted the old array shape, and
tighten the field assertions to token-based matching (no substring matches).

fixes PRD-437

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@christophebrun-forest christophebrun-forest force-pushed the fix/prd-437-agent-client-fields-projection-csv branch from b12a18c to f73aeb3 Compare June 4, 2026 06:53
});

it('should serialize fields as a single comma-separated value, not an array', () => {
// Regression (PRD-437): an array serializes as repeated params that Ruby agents collapse to one.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Opus 4.8[Violates conventions] Comments must not reference PRD/Linear ticket numbers, including in tests. Drop the (PRD-437) token, e.g.:

// Regression: an array serializes as repeated params that Ruby agents collapse to one.

The source comment in query-serializer.ts correctly avoids the reference; this is the only ticket reference in the changed files.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comments must not reference PRD/Linear ticket numbers, per project
conventions and reviewer feedback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@christophebrun-forest christophebrun-forest merged commit 9e0434a into feat/prd-214-server-step-mapper Jun 5, 2026
29 of 30 checks passed
@christophebrun-forest christophebrun-forest deleted the fix/prd-437-agent-client-fields-projection-csv branch June 5, 2026 13:29
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