Skip to content

fix: preserve loom csv retry quota - #2028

Merged
richiemcilroy merged 1 commit into
CapSoftware:mainfrom
tylergibbs1:agent/fix-loom-csv-retries
Jul 24, 2026
Merged

fix: preserve loom csv retry quota#2028
richiemcilroy merged 1 commit into
CapSoftware:mainfrom
tylergibbs1:agent/fix-loom-csv-retries

Conversation

@tylergibbs1

@tylergibbs1 tylergibbs1 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

what changed

csv retries now run the existing loom duplicate lookup before charging a rate limit token. rows with an existing cap return the same duplicate result, while new and stale rows still go through the limiter.

the limiter moved into importLoomVideoForOwner for the csv path. this keeps the lookup and stale row handling in one place and avoids another database query.

the regression test covers a duplicate row followed by a new row and checks that only the new import spends quota.

why

a retry of a partially imported csv spent its full budget on rows that had already succeeded. large imports could never reach the first unfinished row unless someone edited completed rows out of the csv.

closes #2010

testing

  • pnpm --filter @cap/web test
  • pnpm --dir apps/web exec next typegen
  • pnpm exec tsc -b apps/web/tsconfig.json --pretty false
  • pnpm exec biome check apps/web/actions/loom.ts apps/web/__tests__/unit/loom-import.test.ts

Greptile Summary

This PR preserves Loom CSV retry quota by moving per-row rate-limit checks behind the existing duplicate lookup.

  • Already-imported Loom rows now return the existing duplicate result without consuming quota.
  • New and stale rows continue through the rate limiter before import processing.
  • A regression test verifies that a duplicate row followed by a new row charges quota only for the new import.

Confidence Score: 5/5

The PR appears safe to merge, with duplicate retries bypassing quota consumption while importable rows remain rate-limited.

The changed ordering preserves existing duplicate and stale-record behavior, keeps the single-video path unchanged, and applies the CSV limiter before any new import begins.

Important Files Changed

Filename Overview
apps/web/actions/loom.ts Moves the CSV rate-limit callback into the shared import helper after live-duplicate detection while retaining checks for new and stale imports.
apps/web/tests/unit/loom-import.test.ts Adds coverage confirming that duplicate CSV retries preserve quota and a subsequent new row still starts its import.

Reviews (1): Last reviewed commit: "fix: preserve loom csv retry quota" | Re-trigger Greptile

already imported rows spent the per-user budget before the duplicate check, so retries could make no progress on CapSoftware#2010.

it("skips rate limit checks for csv rows that were already imported", async () => {
whereMock.mockImplementation((conditions: unknown) => {
const serializedConditions = JSON.stringify(conditions);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

JSON.stringify(conditions) feels pretty brittle here (and can throw if the underlying Drizzle condition ever gains a circular ref). Since the where() call order is deterministic in this test, stubbing the 4 calls explicitly should be more robust.

Suggested change
const serializedConditions = JSON.stringify(conditions);
whereMock
.mockReturnValueOnce(
withLimit([{ userId: "member-123", email: "member@example.com" }]),
)
.mockResolvedValueOnce([{ videoId: "existing-video" }])
.mockReturnValueOnce(
withLimit([{ userId: "member-123", email: "member@example.com" }]),
)
.mockResolvedValueOnce([]);

error: undefined,
});
expect(checkRateLimitMock).toHaveBeenCalledTimes(1);
expect(fetchMock).toHaveBeenCalled();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Minor: expect(fetchMock).toHaveBeenCalled() doesn’t really assert that the duplicate row avoided doing Loom network work. Might be worth tightening this to ensure no calls include loom-existing123.

Suggested change
expect(fetchMock).toHaveBeenCalled();
expect(fetchMock).toHaveBeenCalled();
expect(
fetchMock.mock.calls.some(([input]) => {
const url = typeof input === "string" ? input : input.toString();
return url.includes("loom-existing123");
}),
).toBe(false);

@richiemcilroy
richiemcilroy merged commit 23a921b into CapSoftware:main Jul 24, 2026
6 of 7 checks passed
@richiemcilroy

Copy link
Copy Markdown
Member

thanks for your contribution!

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.

CSV Loom import: rate-limit tokens consumed before dedupe check, making retries import nothing

2 participants