Skip to content

UN-3770 [FEAT] Opt-in pagination for list endpoints; wire Workflows page - #2187

Merged
chandrasekharan-zipstack merged 6 commits into
mainfrom
feat/UN-3770-list-pagination
Jul 21, 2026
Merged

UN-3770 [FEAT] Opt-in pagination for list endpoints; wire Workflows page#2187
chandrasekharan-zipstack merged 6 commits into
mainfrom
feat/UN-3770-list-pagination

Conversation

@chandrasekharan-zipstack

@chandrasekharan-zipstack chandrasekharan-zipstack commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

What & why

Jira: UN-3770

Four listing endpoints — workflows, prompt studio, adapters, connectors — return every row as a bare array and filter client-side, so the pages get slow to load as an org grows (noticeably Prompt Studio projects and Workflows). This adds opt-in server-side pagination.

The regression constraint

These endpoints are shared. Besides the listing page they feed dropdowns/selectors that expect a bare array:

  • /adapter?adapter_type=…CombinedOutput, OutputForDocModal, AdapterSelectionModal, AddLlmProfile, DefaultTriad
  • connector/ConfigureDs
  • workflow/ list → EtlTaskDeploy

Attaching a DRF pagination_class unconditionally would turn all their responses into the {count,next,previous,results} envelope and break every one of those consumers.

Approach

  • OptionalPagination (utils/pagination.py): paginates only when the request carries ?page/?page_size, otherwise returns None → DRF serialises the bare array. Only the listing page opts in.
  • Attached to the 4 viewsets + server-side name search (?search= icontains).
  • WorkflowViewSet gets a deterministic order (-modified_at, id) — its manager uses plain .distinct() with no default order, which pagination needs for stable pages. The other three already order by DISTINCT ON (id/tool_id).
  • Frontend: Workflows page only — server-side page + search, reusing the existing usePaginatedList hook and the Pipelines wiring pattern. Bare-array fallback (data.results ?? data) kept.

Scope / follow-ups

This is the first vertical slice. The prompt-studio, adapters and connectors frontend pages are intentionally not converted yet — their backends already ship the (dormant) pagination, and each page has intricate search/refresh flows that warrant live validation one at a time under the same ticket.

Testing

  • backend/utils/tests/test_pagination.py pins the opt-in contract (bare array without a page param; envelope with one). Verified standalone (backend test tier needs the full env).
  • Backend ruff + frontend biome clean.
  • ⚠️ Live UX validation of the Workflows page (search, page nav, refresh-after-create/delete/share) still pending — hence draft.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XzUpb2Dyunz2DCdwegR62Z

Four list endpoints (workflows, prompt studio, adapters, connectors)
returned every row as a bare array and filtered client-side, making the
pages slow to load as orgs grow.

Backend: add `OptionalPagination` — it paginates only when the caller sends
`?page`/`?page_size`, otherwise returns None so DRF serialises the bare
array. These endpoints are shared with dropdown/selector consumers that
expect an array, so the opt-in keeps their responses byte-identical while
the listing page opts in and receives the {count,next,previous,results}
envelope. Attach it to the four viewsets and add server-side name search;
Workflow also gets a deterministic order (id tiebreaker) for stable pages.

Frontend: the Workflows page now paginates + searches server-side, reusing
the existing `usePaginatedList` hook and the Pipelines wiring pattern. This
is the first vertical slice — the prompt-studio, adapters and connectors
pages will follow (their backends already ship the dormant pagination).

Test: utils/tests/test_pagination.py pins the opt-in contract (bare array
without a page param; envelope with one).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XzUpb2Dyunz2DCdwegR62Z
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bfb1e2e5-9134-41e9-a10b-b9e5657bb0fc

📥 Commits

Reviewing files that changed from the base of the PR and between aecd00a and 1c77732.

📒 Files selected for processing (2)
  • backend/utils/tests/test_pagination.py
  • frontend/src/components/workflows/workflow/Workflows.jsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/workflows/workflow/Workflows.jsx

Summary by CodeRabbit

  • New Features
    • Added server-side name search for adapters, connectors, tools, and workflows.
    • Introduced optional pagination for list endpoints (bare results unless pagination is explicitly requested).
    • Added deterministic ordering (including id tie-breakers) to stabilize pagination.
  • Bug Fixes
    • Workflow list refresh preserves active page/search across edit, delete, and share; avoids showing an empty page after deleting the last item.
    • Updated workflow pagination/search UI behavior and empty-state messaging.
  • Tests
    • Added contract tests for optional pagination opt-in and edge cases.
  • Chores
    • Minor UI styling updates for workflow pagination layout.

Walkthrough

The change adds opt-in pagination and server-side name search to backend list endpoints, then updates the workflow frontend to request, render, and refresh paginated results while preserving bare-array responses when pagination is not requested.

Changes

Pagination and search flow

Layer / File(s) Summary
Optional pagination contract
backend/utils/pagination.py, backend/utils/tests/test_pagination.py
Adds opt-in pagination when page or page_size is supplied, with tests covering blank values, slicing, counts, and maximum page sizes.
Backend list filtering and ordering
backend/*/views.py
Configures optional pagination, adds case-insensitive name search, and applies deterministic ordering to adapter, connector, tool, and workflow listings.
Workflow paginated list integration
frontend/src/components/workflows/workflow/Workflows.jsx, frontend/src/components/workflows/workflow/workflow-service.js, frontend/src/components/workflows/workflow/Workflows.css
Passes pagination and search parameters, normalizes responses, refreshes after mutations, renders loading and empty states, and displays styled pagination controls.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Workflows
  participant usePaginatedList
  participant workflowService
  participant WorkflowViewSet
  Workflows->>usePaginatedList: request page, page size, and search
  usePaginatedList->>workflowService: getProjectList(params)
  workflowService->>WorkflowViewSet: GET workflow list
  WorkflowViewSet-->>workflowService: filtered results and pagination metadata
  workflowService-->>Workflows: update project list and pagination state
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the opt-in pagination rollout and Workflows frontend wiring.
Description check ✅ Passed It covers the main What/Why, approach, breakage, scope, and testing details; only some optional template sections are omitted.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/UN-3770-list-pagination

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

SonarCloud python:S5332 flags the clear-text http:// scheme in the
request-stub build_absolute_uri; the scheme is inert in this DB-free
contract test but tripping the B security rating on the PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XzUpb2Dyunz2DCdwegR62Z
@chandrasekharan-zipstack
chandrasekharan-zipstack marked this pull request as ready for review July 20, 2026 15:15
@greptile-apps

greptile-apps Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds opt-in pagination for shared list endpoints and wires it into the Workflows page. The main changes are:

  • Optional pagination that keeps bare-array responses unless pagination is requested.
  • Server-side search and deterministic ordering for workflows, prompt studio, adapters, and connectors.
  • Workflows page pagination, search, refresh handling, and pagination control styling.
  • Contract tests for paginated, non-paginated, and blank-parameter requests.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
backend/utils/pagination.py Adds optional pagination that preserves bare-array responses when no pagination parameter is provided.
backend/utils/tests/test_pagination.py Adds contract tests for the opt-in pagination behavior and blank pagination parameters.
backend/workflow_manager/workflow_v2/views.py Adds optional pagination, server-side workflow search, and deterministic workflow ordering.
backend/adapter_processor_v2/views.py Adds optional pagination, adapter name search, and deterministic adapter ordering.
backend/connector_v2/views.py Adds optional pagination, connector name search, and deterministic connector ordering.
backend/prompt_studio/prompt_studio_core_v2/views.py Adds optional pagination, prompt studio search, and deterministic prompt studio ordering.
frontend/src/components/workflows/workflow/Workflows.jsx Updates the Workflows page to fetch paginated results, run server-side search, and refresh the active page after mutations.
frontend/src/components/workflows/workflow/workflow-service.js Updates the workflow list API helper to send pagination and search parameters.
frontend/src/components/workflows/workflow/Workflows.css Adds layout styling for the Workflows pagination control.

Reviews (4): Last reviewed commit: "UN-3770 [FIX] Lock blank-pagination-para..." | Re-trigger Greptile

Comment thread backend/utils/pagination.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
frontend/src/components/workflows/workflow/Workflows.jsx (1)

2-2: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove unused Typography import.

The Typography component from antd is imported but never used in this file.

♻️ Proposed refactor
-import { Pagination, Typography } from "antd";
+import { Pagination } from "antd";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/workflows/workflow/Workflows.jsx` at line 2, Remove
the unused Typography import from the antd import declaration in Workflows.jsx,
while retaining the Pagination import.
frontend/src/components/workflows/workflow/workflow-service.js (1)

27-34: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Declare options to prevent global variable leakage.

The options object is assigned without a variable declaration, which creates an implicit global variable. While this appears to be a pre-existing pattern in this service file, it is best practice to declare variables locally.

♻️ Proposed refactor
     getProjectList: (params = {}) => {
-      options = {
+      const options = {
         url: `${path}/workflow/`,
         method: "GET",
         params,
       };
       return axiosPrivate(options);
     },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/workflows/workflow/workflow-service.js` around lines
27 - 34, Declare options locally within getProjectList before assigning the
request configuration, preventing implicit global leakage while preserving the
existing axiosPrivate call and request parameters.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/adapter_processor_v2/views.py`:
- Around line 193-196: Ensure stable pagination ordering after the for_user()
queryset filtering: in backend/adapter_processor_v2/views.py lines 193-196,
backend/connector_v2/views.py lines 108-112, and
backend/prompt_studio/prompt_studio_core_v2/views.py lines 177-180, apply an
explicit descending modified_at ordering with ascending id as the unique
tiebreaker before returning each queryset (including qs in the prompt studio
view).

In `@frontend/src/components/workflows/workflow/Workflows.jsx`:
- Around line 155-163: Remove the unconditional handleListRefresh() call after
setAlertDetails in the workflow update success handler, preserving the existing
refresh inside the editingProject?.name branch while retaining the refresh
behavior for non-edit updates.
- Around line 139-141: Update the getProjectList failure handler in the workflow
component to set projectList to an empty array before logging the error,
ensuring the existing projectList-based rendering shows the empty state rather
than an indefinite SpinnerLoader after the initial request fails.
- Around line 128-137: Update the paginated workflow fetch handling around the
results assignment and setProjectList call so an empty results array on a page
greater than the first automatically refetches or navigates to the previous page
before updating the displayed list. Keep page 1 empty behavior unchanged, and
ensure pagination state reflects the fallback page so controls remain usable.

---

Nitpick comments:
In `@frontend/src/components/workflows/workflow/workflow-service.js`:
- Around line 27-34: Declare options locally within getProjectList before
assigning the request configuration, preventing implicit global leakage while
preserving the existing axiosPrivate call and request parameters.

In `@frontend/src/components/workflows/workflow/Workflows.jsx`:
- Line 2: Remove the unused Typography import from the antd import declaration
in Workflows.jsx, while retaining the Pagination import.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8c025dc8-c7d4-4a4e-b448-90c184af9b36

📥 Commits

Reviewing files that changed from the base of the PR and between 4eb8811 and dab4e69.

📒 Files selected for processing (9)
  • backend/adapter_processor_v2/views.py
  • backend/connector_v2/views.py
  • backend/prompt_studio/prompt_studio_core_v2/views.py
  • backend/utils/pagination.py
  • backend/utils/tests/test_pagination.py
  • backend/workflow_manager/workflow_v2/views.py
  • frontend/src/components/workflows/workflow/Workflows.css
  • frontend/src/components/workflows/workflow/Workflows.jsx
  • frontend/src/components/workflows/workflow/workflow-service.js

Comment thread backend/adapter_processor_v2/views.py Outdated
Comment thread frontend/src/components/workflows/workflow/Workflows.jsx
Comment thread frontend/src/components/workflows/workflow/Workflows.jsx
Comment thread frontend/src/components/workflows/workflow/Workflows.jsx Outdated
chandrasekharan-zipstack and others added 2 commits July 20, 2026 21:14
…pty-page fallback

- OptionalPagination: treat blank ?page=/?page_size= as not opting in so shared
  callers keep their bare-array response (Greptile).
- adapter/connector/prompt-studio get_queryset: order by the DISTINCT ON field
  for deterministic pagination, incl. the admin/service branch that has no
  distinct; not modified_at (conflicts with DISTINCT ON) (CodeRabbit).
- Workflows.jsx: step back a page when a delete empties the current page
  (CodeRabbit).
- Drop redundant "server-side name search" WHAT comments.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XzUpb2Dyunz2DCdwegR62Z
…tch failure

- Drop the unconditional handleListRefresh() after edit; the branch already
  refreshes existing edits and navigates for new ones (CodeRabbit).
- Default projectList to [] on fetch failure so the empty state shows instead of
  an indefinite spinner (CodeRabbit).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XzUpb2Dyunz2DCdwegR62Z

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/components/workflows/workflow/Workflows.jsx (1)

148-150: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Prevent early clearance of loading state during chained requests.

Using an unconditional .finally() block to clear the shared loading state inadvertently hides the spinner when a chained operation has already initiated a new request. This causes the UI to temporarily freeze while the second network call is in flight.

  • frontend/src/components/workflows/workflow/Workflows.jsx#L148-L150: In getProjectList, finally clears the loading state even when falling back to the previous page (getProjectList(page - 1)). Remove .finally() and explicitly call setLoading(false) inside .then() (after state updates, skipping the fallback return) and .catch().
  • frontend/src/components/workflows/workflow/Workflows.jsx#L179-L181: In editProject, finally clears the loading state immediately after handleListRefresh() triggers a new list fetch. Remove .finally() and let the list refresh manage the loading state, while explicitly clearing it in the openProject and .catch() paths.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/workflows/workflow/Workflows.jsx` around lines 148 -
150, The loading state is cleared prematurely by unconditional finally blocks
during chained requests. In
frontend/src/components/workflows/workflow/Workflows.jsx lines 148-150, update
getProjectList to remove finally and call setLoading(false) in then after state
updates, except before returning for the previous-page fallback, and in catch.
In lines 179-181, update editProject to remove finally, let handleListRefresh
manage loading after refresh, and explicitly clear loading in the openProject
and catch paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@frontend/src/components/workflows/workflow/Workflows.jsx`:
- Around line 148-150: The loading state is cleared prematurely by unconditional
finally blocks during chained requests. In
frontend/src/components/workflows/workflow/Workflows.jsx lines 148-150, update
getProjectList to remove finally and call setLoading(false) in then after state
updates, except before returning for the previous-page fallback, and in catch.
In lines 179-181, update editProject to remove finally, let handleListRefresh
manage loading after refresh, and explicitly clear loading in the openProject
and catch paths.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7153dbac-8d3d-4cc9-ab52-3e1ac91d512a

📥 Commits

Reviewing files that changed from the base of the PR and between dab4e69 and 845f2a7.

📒 Files selected for processing (7)
  • backend/adapter_processor_v2/views.py
  • backend/connector_v2/views.py
  • backend/prompt_studio/prompt_studio_core_v2/views.py
  • backend/utils/pagination.py
  • backend/utils/tests/test_pagination.py
  • backend/workflow_manager/workflow_v2/views.py
  • frontend/src/components/workflows/workflow/Workflows.jsx
💤 Files with no reviewable changes (1)
  • backend/workflow_manager/workflow_v2/views.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • backend/utils/pagination.py
  • backend/utils/tests/test_pagination.py
  • backend/prompt_studio/prompt_studio_core_v2/views.py
  • backend/adapter_processor_v2/views.py
  • backend/connector_v2/views.py

Comment thread backend/utils/pagination.py
…e size

Cover the mixed blank/non-blank pagination params. A blank ?page= alongside a
real ?page_size= serves the first page rather than raising NotFound, since DRF
resolves the page number as `query_params.get("page") or 1`.

Also drop the Workflows page size from 12 to 10 to match the other paginated
listing pages, which use the usePaginatedList default.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XzUpb2Dyunz2DCdwegR62Z
@github-actions

Copy link
Copy Markdown
Contributor

Frontend Lint Report (Biome)

All checks passed! No linting or formatting issues found.

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

Unstract test results

Per-group results

Status Group Tier Passed Failed Errors Skipped Duration (s)
e2e-api-deployment e2e 3 0 0 0 16.3
e2e-coowners e2e 1 0 0 0 1.3
e2e-etl e2e 1 0 0 0 8.3
e2e-login e2e 2 0 0 0 1.2
e2e-prompt-studio e2e 1 0 0 0 5.0
e2e-smoke e2e 2 0 0 0 1.1
e2e-workflow e2e 1 0 0 0 16.5
integration-backend integration 124 0 0 27 53.3
integration-connectors integration 1 0 0 7 6.3
unit-backend unit 160 0 0 0 23.3
unit-connectors unit 63 0 0 0 11.0
unit-core unit 27 0 0 0 1.3
unit-platform-service unit 15 0 0 0 2.9
unit-rig unit 76 0 0 0 4.5
unit-sdk1 unit 490 0 0 0 22.0
unit-workers unit 723 0 0 0 39.4
TOTAL 1690 0 0 34 213.7

Critical paths

⚠️ Critical paths not yet covered

  • workflow-execution-fan-out — Multi-file workflow execution fans out to file-processing workers and rejoins. (declared coverage: no groups declared)
✅ Covered critical paths
  • auth-login — covered by e2e-login
  • adapter-register-llm — covered by integration-backend
  • workflow-author — covered by integration-backend
  • co-owner-manage — covered by integration-backend, e2e-coowners
  • workflow-create-execute — covered by e2e-workflow
  • api-deployment-provision — covered by integration-backend
  • api-deployment-auth — covered by integration-backend
  • api-deployment-run — covered by e2e-api-deployment
  • prompt-studio-author — covered by integration-backend
  • prompt-studio-fetch-response — covered by e2e-prompt-studio
  • connector-register-test — covered by integration-backend
  • pipeline-etl-execute — covered by e2e-etl
  • usage-aggregate-read — covered by integration-backend
  • usage-token-tracking — covered by e2e-api-deployment
  • callback-result-delivery — covered by e2e-api-deployment

@chandrasekharan-zipstack
chandrasekharan-zipstack merged commit eda02f4 into main Jul 21, 2026
12 checks passed
@chandrasekharan-zipstack
chandrasekharan-zipstack deleted the feat/UN-3770-list-pagination branch July 21, 2026 11:49
kirtimanmishrazipstack added a commit that referenced this pull request Jul 28, 2026
…default modified-desc sort

Add a sortable Modified column and rename Created Date -> Created so both dates are visible. Surface the already-serialized prompt_count as "Prompts: N" on the Prompt Studio list. Default all resource lists to modified-desc so the visible Modified column matches the sort (restores #2187 Workflows ordering). Frontend-only; backend already served both dates and prompt_count.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
kirtimanmishrazipstack added a commit that referenced this pull request Jul 28, 2026
* UN-3769 [FEAT] Sortable resource list table with server-side sort, search & pagination

Replace the sparse ListView/ViewTools list UI with a shared sortable
ResourceTable (Name / Owned By / Created Date / Actions) across Adapters,
Workflows, Prompt Studio and Connectors. The Owned By column shows the owner
avatar/name/email plus co-owner count and opens the co-owner modal.

Sort (name/owner/created via the header dropdowns), owner-inclusive search and
pagination are server-driven through a new apply_search_and_sort helper, whose
pk__in re-wrap lifts the Postgres DISTINCT ON each for_user() manager carries so
any column is orderable. Prompt Studio re-applies its prompt_count annotation
after the re-wrap. Delete the now-unused ListView and ViewTools.

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

* UN-3769 [FIX] Dedupe list fetch into shared helpers; fix stale-response race

Resolve SonarCloud/Greptile/CodeRabbit review on the resource-list rollout:

- Extract buildPagedParams + applyPagedResponse into usePaginatedList so the
  four list pages stop copy-pasting the params/response blocks (clears the
  SonarCloud new-code duplication gate).
- applyPagedResponse drops stale responses via a per-page sequence token so a
  slow older request can't overwrite a newer query, and returns the empty-page
  stepback refetch so loading isn't cleared before replacement data arrives.
- ResourceTable detects image icons by URL/data scheme instead of length, so
  compound (ZWJ) emoji no longer render as a broken <img>.
- list_query lowercases sort_by before the dict lookup (matches order handling).
- ToolSettings resets loading when a delete request fails.

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

* UN-3769 [FIX] Collapse duplicated list-page preamble to clear duplication gate

The first review pass left SonarCloud at 8.4% new-code duplication; the real
duplicated blocks were the per-page preamble and the co-owner modal JSX, not
the fetch body. Fix both:

- usePaginatedList now owns fetchRef (pages assign fetchRef.current) and returns
  handleListRefresh, so pages drop their local fetchListRef + identical
  handleListRefresh useCallback.
- Add CoOwnerModal, a thin wrapper mapping a useCoOwnerManagement() bag +
  resourceType onto the CoOwnerManagement modal; the list pages now consume the
  hook as one object and render <CoOwnerModal .../> instead of repeating the
  11-prop invocation.

Net ~150 fewer lines; duplication drops well under the 3% gate.

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

* UN-3769 [FIX] Gate list-fetch catch/finally on the request sequence

The seq guard only suppressed stale successful responses; each page's catch and
finally still ran unconditionally, so a superseded request could clear loading
while a newer one was pending, or surface an error for a query the user had
already moved past. Gate both on seq === seqRef.current so only the newest
request owns the loading state and error reporting.

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

* UN-3769 [FIX] Show a retryable error on list-fetch failure; use codePointAt

- On fetch failure the list pages set displayList to [], so a failed initial
  load rendered a misleading "No X available" empty state. Track an explicit
  loadError instead and render a retryable error (Retry refetches the current
  page), so a failure is no longer shown as an empty success.
- colorForSeed uses String#codePointAt over charCodeAt (SonarCloud S7758).

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

* UN-3769 [FIX] Track "Me" in the Owned By cell by displayed owner, not membership

is_owner is true for any OWNER membership, so a co-owner viewing a resource they
didn't create saw "Me" over the primary owner's avatar/email. Key the "Me" label
on the displayed owner email instead; the creator viewing their own resource
still reads "Me" via the email match.

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

* UN-3769 [FIX] Gate delete-failure loading clear on the request sequence

The adapter delete catch cleared isLoading unconditionally, so a failed delete
could hide the spinner for a newer in-flight fetch (search/sort/paginate/refresh)
and expose obsolete results. Snapshot the request token at delete start and clear
loading only if no newer fetch has taken it over.

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

* UN-3769 [FIX] Keep adapter delete out of the shared list-loading state

ToolSettings was the only list driving the shared isLoading from a row delete,
which produced a string of overlap races (stuck loading, clobbering a newer
fetch, concurrent deletes clearing each other). Drop loading from the delete
entirely, matching the other four lists: success refetches via handleListRefresh
(which owns the spinner), failure just toasts. Removes the race class by
construction rather than adding another guard.

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

* UN-3769 [FIX] Refresh the current list view, not the params captured earlier

handleListRefresh closed over pagination/search/sort, so a refresh captured in a
pending mutation's .then (e.g. a delete) would refetch the stale page/search/order
and overwrite the view the user had since navigated to. Make it a stable callback
that reads the latest params from a ref, so post-mutation refresh always targets
the current view. Fixes it for every list's create/edit/import/delete.

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

* UN-3769 [FIX] Fix list fetch state handling and dead pagination

Pipelines and API deployments still passed the removed `fetchData` option, so
the hook's `fetchRef` stayed null and their pagination and search were silent
no-ops. Both now assign `fetchRef` directly and drop their local `fetchListRef`.

Route every fetch (navigation, last-page stepback, adapter-type reset) through
`requestList`, so the recorded request params always match what lands on screen
and a post-mutation refresh replays the view the user actually asked for.

Realign those params with the displayed view when the newest request fails, so
a failed navigation can't leave a later refresh jumping to a page that never
loaded. Give the workflow edit modal its own loading flag so saving no longer
drives the shared list spinner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3769 [FIX] Address self-review on resource list views

- Owned By names a live owner: owner_email() on HasMembersMixin + 4 list
  serializers, instead of created_by which can be a removed creator.
- Retryable load error is reachable after the first load (gate loadError
  ahead of the length branches on all 4 pages).
- Workflows list-fetch failure surfaces via handleException, not a bare
  console.error.
- Correct usePaginatedList appliedRef comment; requestList returns the
  fetch promise so applyPagedResponse's documented stepback holds.
- Fix stale prompt_count Subquery rationale comment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3769 [FIX] Match resource-table owner avatars to Figma pastel palette

Swap the saturated avatar swatches for light pastel fills paired with a
darker same-hue initial, matching the design. Applies to all resource
list views via the shared ResourceTable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3769 [FIX] Lighten resource-table owner-avatar initials per design

Lighten avatar initial color (Ant -7 -> -6) and reduce initial size
(12px -> 11px) per design review feedback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3769 [FEAT] Resource list: add Modified column + PS prompt count, default modified-desc sort

Add a sortable Modified column and rename Created Date -> Created so both dates are visible. Surface the already-serialized prompt_count as "Prompts: N" on the Prompt Studio list. Default all resource lists to modified-desc so the visible Modified column matches the sort (restores #2187 Workflows ordering). Frontend-only; backend already served both dates and prompt_count.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3769 [FEAT] Resource list: relative Modified time, canonical date format, owner search-only

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3769 [FIX] Resource list review fixes: name-only search, clear-sort restores default

Address Chandru's re-review on #2200:
- Owned By is display-only: drop created_by__email from ordering_fields and
  the search Q-filter across the 4 viewsets so search matches the shown owner
  (name-only) instead of the creator, and no dead owner-sort surface remains.
- usePaginatedList: "Clear Sort" restores the default ordering (not an empty
  one) and list mounts request the seeded sort, so the header and rows agree.
- Remove dead code: orphaned useListSearch.js, the dead avatar-initials branch,
  and the unreferenced .listWrapper rule; trim over-narrated comments.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3769 [FIX] Tests: pin name-only search, dropped owner-ordering fallback, owner_email

Cover the review changes in the shared list-pagination contract test:
- ?search= matches the resource name only, not the owner's email.
- ?ordering=created_by__email is a dropped field -> ignored, list stays
  newest-first (had it survived, same-creator rows would be pk-ordered).
- owner_email() names the earliest live OWNER, skips service accounts, None
  with no owner (shared mixin, pinned once).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chandrasekharan-zipstack added a commit that referenced this pull request Jul 31, 2026
… endpoints (#2208)

* UN-3770 [FIX] Make list pagination consistent across shared resource endpoints

#2187 added opt-in pagination to workflows, prompt studio, adapters and
connectors but only wired the Workflows page. The other three could not
follow: their `for_user()` managers used `DISTINCT ON`, which forces
Postgres to order by the distinct expression, so those viewsets were
pinned to `order_by("id")` and could not order by `modified_at`.

Backend
- Swap `.distinct("id")` / `.distinct("tool_id")` for plain `.distinct()`
  in the adapter, connector and prompt studio managers. Every arm of the
  sharing predicate is a PK subquery, not a join, so no duplicate rows
  exist to collapse and the swap is behaviour-preserving. Workflows has
  shipped this way since #1462.
- Replace the per-view `order_by()` calls with a declarative
  `ordering = ["-modified_at", "pk"]`. `OrderingFilter` is already in
  `DEFAULT_FILTER_BACKENDS`, so this needs no `filter_backends` override
  (which would drop `OrganizationFilterBackend`).
- Drop Workflow's `?order_by=asc|desc`; it has no consumer, and
  `?ordering=` now covers it through the standard filter.

Frontend
- Add `unwrapList` / `fetchAllPages` helpers. Selectors page to
  exhaustion rather than silently showing only the first 50 rows.
- Route all adapter, connector and workflow selectors through them.
- Convert the Prompt Studio, adapters and connectors pages to
  server-side pagination and search via `usePaginatedList`, replacing the
  client-side `useListSearch` filter (now deleted).
- Move `<Pagination>` into `ViewTools` so all four pages share one
  implementation: page size 10, size changer on, `["10","20","50"]`.
  Workflows had the changer disabled; that inconsistency goes away.
- Stop assigning `fetchListRef.current` during render in Workflows.

Endpoints stay opt-in paginated here, so every change is safe against
both response shapes. Flipping them to unconditional `CustomPagination`
is a separate change, after this has been validated on staging.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HXYFVCZGi7YN9qjDGyJCQ

* UN-3770 [FIX] Address review: pk tie-breaker on client ordering, shared list hook

Backend:
- DeterministicOrderingFilter appends `pk` to whatever ordering is in
  effect. `?ordering=` replaces the view's `ordering` outright, so the
  tie-breaker has to be applied by the filter rather than declared on the
  view. Swapped into DEFAULT_FILTER_BACKENDS; a no-op for views that
  declare no ordering and receive no `?ordering=`.
- Tests assert the returned sequence rather than set membership, and pin
  the tie-breaker with rows sharing one `modified_at`. Both fail without
  the filter.

Frontend:
- usePaginatedResource owns the request for all four list pages: params,
  unwrapping, the empty-page step back, and the loading flag. Replaces
  four copy-pasted fetch functions (the Sonar duplication) and the
  fetchListRef indirection.
- Only the newest request may write state, so a slow response can no
  longer restore the previous page, search term or adapter type.
- The loading flag is held by the superseding request, so it no longer
  clears while the step-back page is still in flight.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HXYFVCZGi7YN9qjDGyJCQ

* UN-3770 [MISC] Scope to backend + selectors; drop listing-page conversions

The Prompt Studio, adapters and connectors listing pages are being replaced
wholesale by the ResourceTable in #2200. Converting them here to
usePaginatedResource only to have that work overwritten created the entire
conflict surface between the two PRs, plus a second pagination hook.

Reverts the four listing-page conversions, the ViewTools pagination move and
the useListSearch deletion, and drops usePaginatedResource. What remains is
the part #2200 depends on and cannot do itself: the DISTINCT ON removal,
declarative ordering with a pk tie-breaker, and the selector page-following
that keeps dropdowns whole once pagination goes unconditional.

usePaginatedResource.test.js goes with the hook; its coverage should be
ported onto the surviving usePaginatedList in #2200.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159NvRHFywvkNGji8ECQqeV

* UN-3769 [FEAT] Sortable resource lists with co-owner ownership (#2200)

* UN-3769 [FEAT] Sortable resource list table with server-side sort, search & pagination

Replace the sparse ListView/ViewTools list UI with a shared sortable
ResourceTable (Name / Owned By / Created Date / Actions) across Adapters,
Workflows, Prompt Studio and Connectors. The Owned By column shows the owner
avatar/name/email plus co-owner count and opens the co-owner modal.

Sort (name/owner/created via the header dropdowns), owner-inclusive search and
pagination are server-driven through a new apply_search_and_sort helper, whose
pk__in re-wrap lifts the Postgres DISTINCT ON each for_user() manager carries so
any column is orderable. Prompt Studio re-applies its prompt_count annotation
after the re-wrap. Delete the now-unused ListView and ViewTools.

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

* UN-3769 [FIX] Dedupe list fetch into shared helpers; fix stale-response race

Resolve SonarCloud/Greptile/CodeRabbit review on the resource-list rollout:

- Extract buildPagedParams + applyPagedResponse into usePaginatedList so the
  four list pages stop copy-pasting the params/response blocks (clears the
  SonarCloud new-code duplication gate).
- applyPagedResponse drops stale responses via a per-page sequence token so a
  slow older request can't overwrite a newer query, and returns the empty-page
  stepback refetch so loading isn't cleared before replacement data arrives.
- ResourceTable detects image icons by URL/data scheme instead of length, so
  compound (ZWJ) emoji no longer render as a broken <img>.
- list_query lowercases sort_by before the dict lookup (matches order handling).
- ToolSettings resets loading when a delete request fails.

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

* UN-3769 [FIX] Collapse duplicated list-page preamble to clear duplication gate

The first review pass left SonarCloud at 8.4% new-code duplication; the real
duplicated blocks were the per-page preamble and the co-owner modal JSX, not
the fetch body. Fix both:

- usePaginatedList now owns fetchRef (pages assign fetchRef.current) and returns
  handleListRefresh, so pages drop their local fetchListRef + identical
  handleListRefresh useCallback.
- Add CoOwnerModal, a thin wrapper mapping a useCoOwnerManagement() bag +
  resourceType onto the CoOwnerManagement modal; the list pages now consume the
  hook as one object and render <CoOwnerModal .../> instead of repeating the
  11-prop invocation.

Net ~150 fewer lines; duplication drops well under the 3% gate.

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

* UN-3769 [FIX] Gate list-fetch catch/finally on the request sequence

The seq guard only suppressed stale successful responses; each page's catch and
finally still ran unconditionally, so a superseded request could clear loading
while a newer one was pending, or surface an error for a query the user had
already moved past. Gate both on seq === seqRef.current so only the newest
request owns the loading state and error reporting.

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

* UN-3769 [FIX] Show a retryable error on list-fetch failure; use codePointAt

- On fetch failure the list pages set displayList to [], so a failed initial
  load rendered a misleading "No X available" empty state. Track an explicit
  loadError instead and render a retryable error (Retry refetches the current
  page), so a failure is no longer shown as an empty success.
- colorForSeed uses String#codePointAt over charCodeAt (SonarCloud S7758).

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

* UN-3769 [FIX] Track "Me" in the Owned By cell by displayed owner, not membership

is_owner is true for any OWNER membership, so a co-owner viewing a resource they
didn't create saw "Me" over the primary owner's avatar/email. Key the "Me" label
on the displayed owner email instead; the creator viewing their own resource
still reads "Me" via the email match.

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

* UN-3769 [FIX] Gate delete-failure loading clear on the request sequence

The adapter delete catch cleared isLoading unconditionally, so a failed delete
could hide the spinner for a newer in-flight fetch (search/sort/paginate/refresh)
and expose obsolete results. Snapshot the request token at delete start and clear
loading only if no newer fetch has taken it over.

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

* UN-3769 [FIX] Keep adapter delete out of the shared list-loading state

ToolSettings was the only list driving the shared isLoading from a row delete,
which produced a string of overlap races (stuck loading, clobbering a newer
fetch, concurrent deletes clearing each other). Drop loading from the delete
entirely, matching the other four lists: success refetches via handleListRefresh
(which owns the spinner), failure just toasts. Removes the race class by
construction rather than adding another guard.

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

* UN-3769 [FIX] Refresh the current list view, not the params captured earlier

handleListRefresh closed over pagination/search/sort, so a refresh captured in a
pending mutation's .then (e.g. a delete) would refetch the stale page/search/order
and overwrite the view the user had since navigated to. Make it a stable callback
that reads the latest params from a ref, so post-mutation refresh always targets
the current view. Fixes it for every list's create/edit/import/delete.

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

* UN-3769 [FIX] Fix list fetch state handling and dead pagination

Pipelines and API deployments still passed the removed `fetchData` option, so
the hook's `fetchRef` stayed null and their pagination and search were silent
no-ops. Both now assign `fetchRef` directly and drop their local `fetchListRef`.

Route every fetch (navigation, last-page stepback, adapter-type reset) through
`requestList`, so the recorded request params always match what lands on screen
and a post-mutation refresh replays the view the user actually asked for.

Realign those params with the displayed view when the newest request fails, so
a failed navigation can't leave a later refresh jumping to a page that never
loaded. Give the workflow edit modal its own loading flag so saving no longer
drives the shared list spinner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3769 [FIX] Address self-review on resource list views

- Owned By names a live owner: owner_email() on HasMembersMixin + 4 list
  serializers, instead of created_by which can be a removed creator.
- Retryable load error is reachable after the first load (gate loadError
  ahead of the length branches on all 4 pages).
- Workflows list-fetch failure surfaces via handleException, not a bare
  console.error.
- Correct usePaginatedList appliedRef comment; requestList returns the
  fetch promise so applyPagedResponse's documented stepback holds.
- Fix stale prompt_count Subquery rationale comment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3769 [FIX] Match resource-table owner avatars to Figma pastel palette

Swap the saturated avatar swatches for light pastel fills paired with a
darker same-hue initial, matching the design. Applies to all resource
list views via the shared ResourceTable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3769 [FIX] Lighten resource-table owner-avatar initials per design

Lighten avatar initial color (Ant -7 -> -6) and reduce initial size
(12px -> 11px) per design review feedback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3769 [FEAT] Resource list: add Modified column + PS prompt count, default modified-desc sort

Add a sortable Modified column and rename Created Date -> Created so both dates are visible. Surface the already-serialized prompt_count as "Prompts: N" on the Prompt Studio list. Default all resource lists to modified-desc so the visible Modified column matches the sort (restores #2187 Workflows ordering). Frontend-only; backend already served both dates and prompt_count.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3769 [FEAT] Resource list: relative Modified time, canonical date format, owner search-only

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3769 [FIX] Resource list review fixes: name-only search, clear-sort restores default

Address Chandru's re-review on #2200:
- Owned By is display-only: drop created_by__email from ordering_fields and
  the search Q-filter across the 4 viewsets so search matches the shown owner
  (name-only) instead of the creator, and no dead owner-sort surface remains.
- usePaginatedList: "Clear Sort" restores the default ordering (not an empty
  one) and list mounts request the seeded sort, so the header and rows agree.
- Remove dead code: orphaned useListSearch.js, the dead avatar-initials branch,
  and the unreferenced .listWrapper rule; trim over-narrated comments.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3769 [FIX] Tests: pin name-only search, dropped owner-ordering fallback, owner_email

Cover the review changes in the shared list-pagination contract test:
- ?search= matches the resource name only, not the owner's email.
- ?ordering=created_by__email is a dropped field -> ignored, list stays
  newest-first (had it survived, same-creator rows would be pk-ordered).
- owner_email() names the earliest live OWNER, skips service accounts, None
  with no owner (shared mixin, pinned once).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* UN-3770 [FIX] Address review: deterministic owner, keyboard row actions, payload guard

- owner_email(): break created_at ties by pk so the "Owned By" label is
  stable across requests; cover equal timestamps in the shared test.
- ResourceTable row actions rendered as non-focusable icon spans; wrap in
  real buttons so edit/share/delete are keyboard reachable and Popconfirm
  gets a focusable trigger.
- applyPagedResponse: guard non-array payloads (204 body, stray object)
  before they reach antd Table dataSource.
- Client-ordering test: two modified_at groups so honored ascending order
  is distinguishable from the -modified_at default, not just the pk tie.
- Drop redundant "# Name search." comments on the name-only search blocks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3770 [FIX] Use aria-disabled on row actions so the deprecated tooltip survives

A native disabled button suppresses hover, hiding the "deprecated" tooltip.
aria-disabled keeps the control focusable and hoverable; the onClick guard
already no-ops when deprecated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3770 [FIX] Polish resource list table: sort affordance + layout

- Don't highlight the default sort column on load; the header lights up
  only once the user explicitly picks a sort. usePaginatedList tracks a
  `userSorted` flag threaded to ResourceTable/SortHeader.
- Name column absorbs the slack while Owned By/Created/Modified/Actions
  share one compact fixed width, so they read as an evenly-spaced group
  and Name stays dominant.
- Owner name/email ellipsize within their cell (drop the 190px cap, let
  the Space item shrink), removing the trailing-gap skew.
- Table scrolls inside its own container below its min-width instead of
  crushing columns on narrow screens.
- Created timestamp gets ellipsis+tooltip as a safety for long values.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HXYFVCZGi7YN9qjDGyJCQ

* UN-3770 [FEAT] Search resource lists by owner too, not just name

Owner search matches the displayed owner — the OWNER membership that backs
the Owned By column — via a search-time subquery (name OR owner email),
across all four shared list endpoints. Reuses the sharing_helpers
varchar/UUID object_id cast. `created_by` stays audit-only (UN-2202);
service accounts and non-owner (VIEWER) members are excluded.

This intentionally reverses the name-only narrowing from UN-3769: search
now agrees with what the Owned By column shows, which was that change's goal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HXYFVCZGi7YN9qjDGyJCQ

* UN-3770 [FEAT] Update search placeholder to "Search by name or owner"

The four owner-searchable list pages now advertise owner search via the
placeholder; ToolNavBar's other consumers keep the "Search by name" default.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HXYFVCZGi7YN9qjDGyJCQ

* UN-3770 [FEAT] List all co-owners in the Owned By tooltip

Adds owner_emails() (all live OWNER emails, earliest-first) on HasMembersMixin,
exposed by the four list serializers. The Owned By cell still shows the primary
owner + `+N` inline, but its tooltip now names every co-owner — so a search that
matched a co-owner hidden behind `+N` is explainable on hover.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HXYFVCZGi7YN9qjDGyJCQ

* UN-3770 [REFACTOR] Collapse owner_email into owner_emails

owner_email was owner_emails[0] — one derivable scalar of redundancy. Drop it
from the four list serializers and read owner_emails[0] on the frontend instead
(ResourceTable, and the cloud Projects card in the companion cloud PR). The
model's owner_email() accessor stays for callers/tests that want just the head.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HXYFVCZGi7YN9qjDGyJCQ

* UN-3770 [REVERT] Drop the table column-width/layout tweaks for now

Revert the layout half of the earlier "sort affordance + layout" change:
restore the proportional column widths (34/22/15/15/14%), the 190px owner
name/email cap, and drop the min-width scroll container + Created ellipsis.
The sort-affordance (userSorted) fix stays. Layout to be revisited later.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HXYFVCZGi7YN9qjDGyJCQ

* UN-3770 [FEAT] ResourceTable: extraColumns + onRowClick (for lookups) (#2221)

UN-3770 [FEAT] ResourceTable: extraColumns + onRowClick override

Let callers inject resource-specific columns (inserted before Actions) and
override the default relative row-click nav — needed so the Prompt Studio
lookups table can reuse this widget while keeping its Files/Latest Version
columns and its absolute, stateful navigation.


Claude-Session: https://claude.ai/code/session_018HXYFVCZGi7YN9qjDGyJCQ

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* chore: re-trigger pre-commit.ci (transient mergeable-check error)

* UN-3770 [FIX] Address review: owner fallback+mask, scoped tiebreaker, index, prefix search

- ResourceTable: fall back to created_by_email so rows with no live OWNER
  membership render the creator instead of "Unknown".
- Adapter serializer: mask owner_emails to ["Unstract"] for frictionless
  adapters so the Owned By column keeps the org-wide mask.
- Drop redundant .distinct() from adapter/connector/prompt_studio for_user
  (every arm is a PK subquery, no join, nothing to collapse).
- Add (organization, -modified_at) index to the 4 resource models backing the
  default list ordering.
- fetchAllPages: request MAX_PAGE_SIZE up front so the common case is one
  round-trip; the loop stays as the tail guard.
- Pin plain OrderingFilter on the two high-volume execution-log endpoints so
  they don't inherit the pk tiebreaker (unindexed Sort) from the global default.
- Owner search matches the email prefix (local part) so a bare domain fragment
  doesn't return every row in a single-domain org.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HXYFVCZGi7YN9qjDGyJCQ

* UN-3770 [FIX] Address Greptile: stable log pagination + drop stale list responses

Two P1 findings from Greptile:

- Execution-log endpoints (file_execution, execution_log_view) dropped their
  plain-OrderingFilter override and now inherit the global deterministic
  filter, so tied created_at/event_time rows can't repeat or omit across pages.
  Each request is already scoped to a single execution_id, so the pk
  tie-breaker sorts a narrow set, not the whole table.

- Pipelines and ApiDeployment list fetches adopt the monotonic seq guard via
  applyPagedResponse, so a slow superseded response can no longer overwrite a
  newer search/page/type selection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HXYFVCZGi7YN9qjDGyJCQ

* UN-3770 [FIX] Route remaining paginated viewsets through deterministic ordering

Greptile P1: ExecutionViewSet still bypassed the global DeterministicOrderingFilter
with a plain OrderingFilter, so tied created_at rows could repeat or omit across
pages. Four other pre-existing paginated viewsets (tags, usage_v2, dashboard_metrics,
pipeline_v2) had the same override. Swapped each to DeterministicOrderingFilter,
appending the pk tie-breaker while keeping their existing backends unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HXYFVCZGi7YN9qjDGyJCQ

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Kirtiman Mishra <110175055+kirtimanmishrazipstack@users.noreply.github.com>
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