Skip to content

fix(agent-bff): reject multi-field sort on express and mongoose lianas - #1970

Open
Tonours wants to merge 2 commits into
mainfrom
hp/gateway-mcp-bff/t-0037-impl-prd-1474-multi-sort
Open

Tonours wants to merge 2 commits into
mainfrom
hp/gateway-mcp-bff/t-0037-impl-prd-1474-multi-sort

Conversation

@Tonours

@Tonours Tonours commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

The BFF now answers a typed 422 instead of a misleading 503 when a multi-field sort targets a liana that can only sort on one field. refs PRD-1474

What

  • On forest-express-sequelize and forest-express-mongoose, sort with more than one clause answers 422 multi_field_sort_not_supported, before the list call. List and relation list.
  • Behavior change on mongoose: that request used to answer 200 with the sort silently ignored. It now answers 422.
  • The served OpenAPI caps sort at maxItems: 1 on those agents and names the error.
  • Rails, v2 and single-field sorts don't change. The 5xx → agent_unavailable mapping doesn't change.

Why

  • Both lianas read sort as one column string. Express fails with Unknown column 'edge_composite_pk.seq,tenant_id' (500), and the BFF relayed it as 503 agent_unavailable. Mongoose sorts on a field that doesn't exist and returns rows unsorted.
  • No wire format fixes this. Comma, repeated key and sort[0] all fail on the liana itself (G0 on the ticket).

How

  • The v1 capability synthesis already states sortable: false from the apimap. It now also states multiFieldSort: false, keyed on the liana name the schema carries (a required argument, so no caller can drop the gate). The validator rejects on it, like field_not_sortable.
  • On a cold cache, the capabilities probe (the liana's 404) still runs before the 422. The list call never does.

Verification

Check Result
agent-bff tests, tsc --noEmit, eslint on 484224e6d ✅ 2188/2188, clean
Independent review (same-family, Claude) on 87a5e36b6 ✅ GO WITH NOTES, notes fixed in 484224e6d
Real-stack e2e on 484224e6d (express, mongoose, rails, v2-ruby OAuth), A/B against main ✅ PASS
e2e: express list + relation list, mongoose list, multi-sort ✅ 422 multi_field_sort_not_supported, 0 list call on the agent (mongoose cold cache: only the capabilities 404 probe)
e2e: single-field sort, all 4 stacks, relations included ✅ order checked in the data, asc and desc
e2e: rails + v2-ruby multi-sort ✅ both keys honored, key 2 checked on key-1 ties (27, 3, 5 ties), list and relation
e2e: OpenAPI maxItems: 1 ✅ 17/17 express and 4/4 mongoose list operations, 0 on rails (19), v2 (23) and main
e2e: A/B ✅ express 503 → 422, mongoose 200 unsorted → 422, rails and v2 identical to main
CI on 484224e6d ✅ all green (31 pass). Earlier red runs were GitHub Actions cancellations, jobs never started

Limits: the literal rails [title,-created_at] case has unique titles, so key 2 can't be observed there (the 3 tied cases prove it). Mongoose relation list is not measurable: the bench schema has no relation. The relation-list integration test asserts only error.type.

Definition of Done

General

  • Write an explicit title for the Pull Request, following Conventional Commits specification
  • Test manually the implemented changes
  • Validate the code quality (indentation, syntax, style, simplicity, readability)

Security

  • Consider the security impact of the changes made

@linear-code

linear-code Bot commented Oct 5, 2026

Copy link
Copy Markdown

PRD-1474

@qltysh

qltysh Bot commented Oct 5, 2026

Copy link
Copy Markdown

Qlty


Coverage Impact

This PR will not change total coverage.

Modified Files with Diff Coverage (5)

RatingFile% DiffUncovered Line #s
Coverage rating: A Coverage rating: A
packages/agent-bff/src/openapi/unfolded-paths.ts100.0%
Coverage rating: A Coverage rating: A
packages/agent-bff/src/read-model/agent-capabilities-fetcher.ts100.0%
Coverage rating: A Coverage rating: A
packages/agent-bff/src/read-model/synthesize-capabilities.ts100.0%
Coverage rating: A Coverage rating: A
packages/agent-bff/src/validation/validation-errors.ts100.0%
Coverage rating: A Coverage rating: A
packages/agent-bff/src/validation/capabilities-validator.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.

This branch has not been deployed

No deployments
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