Skip to content

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

Open
christophebrun-forest wants to merge 1 commit into
feat/prd-214-server-step-mapperfrom
fix/prd-437-agent-client-fields-projection-csv
Open

fix(agent-client): serialize fields[] projection as comma-separated string (PRD-437)#1622
christophebrun-forest wants to merge 1 commit 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.

Macroscope summarized b12a18c.

@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

…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 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.

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