Skip to content

Goal: Product Slow — Front-End Items Page Performance

Lifecycle: Completed

Eliminate the kanban-card N+1 fan-out on the /items page. Today the page issues 1 + 2N backend round-trips per render (1 items/query-ssrm plus 2 kanban-card calls per visible row); a standard 60-row tenant pays 121 round-trips. The target shape is 1 + 1 = 2 per AG Grid SSRM block, achieved by collapsing the per-row kanban-card calls onto a single batched Filter.In(item_reference_entity_id, [eIds…]) query against the existing /v1/kanban/kanban-card/query route. No new operations endpoint is added; the work is frontend-only and depends on a composite bitemporal index that ships from PDEV-490.

This project is the front-end counterpart to the operations-side work in the product-slow-responses umbrella, and is tracked under PDEV-489.

Umbrella and the four concrete deliverables, all in PDEV and assigned to the current cycle:

RepositoryRolePlanned Changes
arda-frontend-appProduction frontend — owner of all code changes/items page restructure, ItemCardsContext expansion, bulk handler rewrite, BFF proxy log cleanup
documentationProject artifactsgoal.md (this file), per-sub-issue specifications and verification reports as the work progresses
workbooksWorking notes and analysisWorking notes captured during implementation; promoted to the documentation repo as canonical patterns once stable
  1. The /items page issues exactly 2 backend round-trips per AG Grid SSRM block on cold load (1 items/query-ssrm plus 1 batched kanban-card/query).
  2. Per-row counts (safeCards.length, inOrderQueueCount, printedCount) and the per-row candidateCard derive from a single batched dataset shared via ItemCardsContext.
  3. Opening ItemDetailsPanel for a row already in the grid paints instantly from the cached ItemCardsContext entry — no per-card secondary GETs (the old query-details-by-item per-card fan-out is gone). A single, parallel refreshCardsForItem(eid) may fire as the spec’s freshness guarantee (paired with the rId-set diff → optional stale banner); that is the refresh, not the per-card N+1.
  4. Bulk handlers (handleDeleteMultipleItems, handlePrintSelectedCards, handlePreviewSelectedCards) issue exactly 1 batched kanban-card call per invocation regardless of selection size.
  5. The two BFF kanban-card proxy routes emit no per-request body or header logs in production builds; structured error logs only.
  6. Page latency on the 60-row test tenant drops from the current baseline to a near-flat profile dominated by the two round-trips per block.
  7. All four sub-issues land as separate PRs against arda-frontend-app with passing CI; the documentation worktree publishes the per-sub-issue verification reports.

The original PDEV-489 proposal (P1–P5, drafted 2026-05-13) assumed a new operations aggregate endpoint (summary-for-items, was K01). PDEV-490 design sessions cancelled K01 on 2026-05-18 after verifying that the existing /v1/kanban/kanban-card/query route already accepts Filter.In(item_reference_entity_id, [eIds…]) and returns every field the frontend row reads. The headline work is now frontend-only.

The frontend collapse relies on a composite bitemporal index (tenant_id, item_reference_entity_id, eid, effective_as_of DESC, recorded_as_of DESC) on kanban_card, shipping as Flyway migration V007 in Arda-cards/operations#173. Frontend work may develop in parallel with that PR’s review/merge cycle, but must not deploy to dev before PR #173 lands or the SQL plan will fall back to sequential scan.

Original P5 (the “500 == no cards” dead branch) is closed transparently by PDEV-490 K12 (cardsForItem withTotal = false), which also lands in operations#173.

  • Frontend restructure of /items page kanban-card traffic onto a single batched Filter.In query per SSRM block.
  • Expansion of ItemCardsContext to serve as the shared cache between the grid, the detail panel, and bulk-action handlers.
  • Cleanup of the two BFF kanban-card proxy route handlers to remove verbose request/response logging.
  • Per-sub-issue specification, verification, and PR-body changelog entries recorded in the documentation worktree.
  • New operations endpoints. The decision is to reuse /v1/kanban/kanban-card/query with Filter.In.
  • Client-side caching of kanban-card data beyond the page-scoped ItemCardsContext (kanban-card data is too change-heavy to cache at the BFF layer; the items list’s separate unstable_cache remains).
  • BFF auth-helper consolidation, the hard-coded User-Agent: PostmanRuntime/7.45.0, and devLog chain rewrites (captured as bycatch in the PDEV-489 body; spin off individually if/when they bite).
  • Operations-side improvements (AWS Advanced JDBC Wrapper, AppError.Transient retry contract). These are independent and ship from PDEV-490.
  1. No new operations routes. The existing /v1/kanban/kanban-card/query is the only kanban-card endpoint touched.

  2. Frontend collapse must not deploy to dev ahead of Arda-cards/operations#173 or the SQL plan will sequential-scan kanban_card.

  3. One shared branch (jmpicnic/product-slow-fe) across all three worktrees for now; per-sub-issue branches may be split out later if parallel work becomes necessary.

  4. Each sub-issue ships as its own PR against arda-frontend-app with the workspace’s standard PR-body changelog entry. The sub-issue PRs form a stack managed with the gh-stack skill — the headline PDEV-235 change is the base of the stack, and the remaining sub-issues stack on top in sequence:

    1. PDEV-235 (base) — items page collapse onto batched Filter.In; targets main.
    2. PDEV-548 — detail panel reads from ItemCardsContext; targets the PDEV-235 branch.
    3. PDEV-549 — bulk handlers onto Filter.In; targets the PDEV-548 branch.
    4. PDEV-550 — BFF proxy log cleanup; targets the PDEV-549 branch.

    The stack ordering follows the data dependency: PDEV-235 introduces the shared itemCardsMap and batched query that PDEV-548 and PDEV-549 consume; PDEV-550 is independent but parked at the top of the stack so it lands last and does not block the latency-critical changes on log cleanup review. As each PR merges into main, the rest of the stack rebases down.


Copyright: (c) Arda Systems 2025-2026, All rights reserved