Skip to content

fix(workflow-executor): read composite-key records via the agent by-id route [PRD-450]#1629

Merged
matthv merged 2 commits into
feat/prd-214-server-step-mapperfrom
fix/prd-450-executor-composite-pk-by-id
Jun 8, 2026
Merged

fix(workflow-executor): read composite-key records via the agent by-id route [PRD-450]#1629
matthv merged 2 commits into
feat/prd-214-server-step-mapperfrom
fix/prd-450-executor-composite-pk-by-id

Conversation

@matthv
Copy link
Copy Markdown
Member

@matthv matthv commented Jun 5, 2026

Problem

On collections with a composite primary key, the workflow executor failed to read records in orchestrator mode with Record not found.

getRecord built the agent filter by zipping two arrays by position:

  • primaryKeyFields — from the orchestrator collection-schema, in alphabetical order (the agent apimap sorts fields alphabetically; is_primary_key is just a boolean, no ordinal);
  • recordId — in model primary_key order.

For a PK whose model order ≠ alphabetical (e.g. Shipping = [order_id, customer_id]), the columns got mis-paired → no match → not found. The executor has no way to recover the real PK order from the schema it receives.

Fix

Stop re-pairing the id in the executor. Read by id through the agent's by-id route (like update/delete already do) and let the agent — the only party that knows the PK column order — do the matching. The recordId is an opaque ordered token; the executor never needs to know the key composition.

  • add Collection#getOne(id, options) to @forestadmin/agent-client (handles 404 / empty as "not found");
  • rewrite getRecord to use it; remove buildPkFilter / resolveSchema;
  • same reasoning for getRelatedData composite recordIds — use the agent's opaque row.id instead of rebuilding from primaryKeyFields.

Coupling

For v1 agents the by-id read needs the agent to accept the pipe-joined id form — forest_liana: ForestAdmin/forest-rails#774 ; forest-express: pending. v2 already supports it.

Testing

  • agent-client-agent-port.test.ts updated (getRecord/getSingleRelatedData now exercise getOne; composite getRelatedData uses the opaque id). Full suite: 887 passing, lint clean.
  • Validated end-to-end: composite-key Shipping workflow (get-data → condition → update-data) reads + updates the record in orchestrator mode.

Relates to PRD-450. The agent-side id-format fix is tracked separately (see PRD-450).

🤖 Generated with Claude Code

Note

Fix composite-key record fetching in AgentClientAgentPort to use the agent by-id route

  • Adds Collection.getOne(id, options) in collection.ts that fetches a single record via GET /forest/<collection>/<serializedId>, returning null on 404 or empty payload.
  • Replaces the list-with-filters approach in AgentClientAgentPort.getRecord with a direct call to getOne, removing the dependency on schema cache and buildPkFilter.
  • Fixes composite primary key handling in getRelatedData by splitting the agent-provided opaque id on '|' instead of reconstructing it from schema field ordering.
  • Removes resolveSchema and buildPkFilter helpers that are no longer needed.

🖇️ Linked Issues

Addresses PRD-450.

Macroscope summarized e47d474.

…d route [PRD-450]

getRecord built the agent filter by zipping primaryKeyFields (alphabetical, from
the orchestrator collection-schema) with the recordId (model primary_key order).
For composite PKs whose model order differs from alphabetical this mis-paired the
columns, so the record was never found ("Record not found").

Fetch by id through the agent's by-id route instead (like update/delete): the
recordId is an opaque ordered token and the agent — the only party that knows the
primary key column order — does the matching. The executor no longer needs to
know the key composition. Same reasoning applied to getRelatedData composite
recordIds (use the agent's opaque row id instead of rebuilding from
primaryKeyFields).

- add Collection#getOne(id, options) to @forestadmin/agent-client (by-id route)
- rewrite getRecord to use it; remove buildPkFilter / resolveSchema

For v1 agents the by-id read needs the agent to accept the pipe-joined id form
(forest_liana: PR #774; forest-express: pending). v2 already supports it.

Relates to PRD-450.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@linear-code
Copy link
Copy Markdown

linear-code Bot commented Jun 5, 2026

PRD-450

Copy link
Copy Markdown
Member

@christophebrun-forest christophebrun-forest left a comment

Choose a reason for hiding this comment

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

OK for me

… into fix/prd-450-executor-composite-pk-by-id

# Conflicts:
#	packages/workflow-executor/src/adapters/agent-client-agent-port.ts
@qltysh
Copy link
Copy Markdown

qltysh Bot commented Jun 8, 2026

Qlty


Coverage Impact

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

Modified Files with Diff Coverage (2)

RatingFile% DiffUncovered Line #s
New Coverage rating: A
packages/agent-client/src/domains/collection.ts0.0%179-189
New Coverage rating: A
...ages/workflow-executor/src/adapters/agent-client-agent-port.ts100.0%
Total44.4%
🤖 Increase coverage with AI coding...
In the `fix/prd-450-executor-composite-pk-by-id` branch, add test coverage for this new code:

- `packages/agent-client/src/domains/collection.ts` -- Line 179-189

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

@matthv matthv merged commit c59feb7 into feat/prd-214-server-step-mapper Jun 8, 2026
30 checks passed
@matthv matthv deleted the fix/prd-450-executor-composite-pk-by-id branch June 8, 2026 10:04
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