diff --git a/.agents/skills/v2-api-conventions/SKILL.md b/.agents/skills/v2-api-conventions/SKILL.md index 15f0caed19e..44d0b298e6c 100644 --- a/.agents/skills/v2-api-conventions/SKILL.md +++ b/.agents/skills/v2-api-conventions/SKILL.md @@ -113,7 +113,7 @@ Every paged list's binding is declared in `lib/api/contracts/v2/__tests__/list-p Return `nextCursor: null` on the last page and only then. Never construct a cursor client-side. -**Ordering is `sortBy` + `sortOrder`, except where there is nothing to sort by.** Fifteen lists take the pair. Exactly one — `GET /workflows/{workflowId}/runs` — has a single sortable column (start time), so there is no `sortBy` to pair with and the direction rides on a single `order` param; `sortBy`/`sortOrder` are not accepted there. That is the *only* sanctioned deviation, and it is documented in its contract. A new list picks the pair. Do not "fix" it by accepting `sortOrder` as an alias: an alias is a second spelling of one thing with undefined precedence when both arrive, which is its own inconsistency. +**Ordering is `sortBy` + `sortOrder`, except where there is nothing to sort by.** Nearly every paged list takes the pair; `CURSOR_BINDINGS` in `contracts/v2/__tests__/list-pagination.test.ts` is the authoritative set. Exactly one — `GET /workflows/{workflowId}/runs` — has a single sortable column (start time), so there is no `sortBy` to pair with and the direction rides on a single `order` param; `sortBy`/`sortOrder` are not accepted there. That is the *only* sanctioned deviation, and it is documented in its contract. A new list picks the pair. Do not "fix" it by accepting `sortOrder` as an alias: an alias is a second spelling of one thing with undefined precedence when both arrive, which is its own inconsistency. `GET /logs` was the second exception until it absorbed `POST /logs/query`. That fold is the cautionary tale for this rule: the justification for the `order` spelling was "logs have exactly one sortable column", and a second endpoint sorting the same rows four ways had already disproved it. When a rule's premise is contradicted by another endpoint on the same collection, fix the premise rather than documenting the exception. diff --git a/apps/sim/app/api/v2/knowledge/[knowledgeBaseId]/documents/route.ts b/apps/sim/app/api/v2/knowledge/[knowledgeBaseId]/documents/route.ts index e4950940d3d..70d57103e8f 100644 --- a/apps/sim/app/api/v2/knowledge/[knowledgeBaseId]/documents/route.ts +++ b/apps/sim/app/api/v2/knowledge/[knowledgeBaseId]/documents/route.ts @@ -54,10 +54,13 @@ const MAX_FILE_SIZE = MAX_KNOWLEDGE_DOCUMENT_FILE_SIZE * stating it or omitting it selects the same documents. A scope part the query * ignores refuses a cursor for a page that did not move. * - * `workspaceId` is dropped for the same reason as on the sibling chunk list: - * the sequence is one knowledge base, named by the path param this already - * binds, and the query's workspace is asserted scope that authorization refuses - * before paging rather than a filter that can select a different sequence. + * `workspaceId` is asserted scope rather than a filter, for the same reason as + * on the sibling chunk list: the sequence is one knowledge base, named by the + * path param this already binds, and authorization refuses any other workspace + * before paging. Unlike that list it is nonetheless kept in the fingerprint — + * see the note at the binding below. Dropping it would be correct in principle + * and inert in effect, but it would refuse every cursor minted before the + * change. */ function documentCursorFilters( knowledgeBaseId: string,