Skip to content

store args/context separately from work when large#170

Merged
ianmacartney merged 12 commits intomainfrom
ian/handle-big-args
Feb 23, 2026
Merged

store args/context separately from work when large#170
ianmacartney merged 12 commits intomainfrom
ian/handle-big-args

Conversation

@ianmacartney
Copy link
Member

@ianmacartney ianmacartney commented Feb 17, 2026

  1. Storing large function arguments and context data separately in a new payload table when they exceed 8KB
  2. Adding a payloadId field to the work table to reference this separate storage
  3. Implementing chunking in enqueueBatch to handle large batches by splitting them into smaller pieces
  4. Updating the cleanup functions to properly handle and delete these separate payload documents
  5. Improving the pagination approach in danger.ts functions to be more bandwidth-aware

These changes allow us to handle much larger function arguments and context data without hitting document size limits, while also making the system more efficient when processing large batches of work.

Summary by CodeRabbit

  • Refactor

    • Optimized storage handling for large job arguments and metadata by automatically storing oversized data separately when exceeding 8KB thresholds.
    • Implemented size-aware batching for job submissions to improve throughput and enforce storage limits (maximum 1MB per item).
    • Enhanced data cleanup processes with iterative limits to prevent resource exhaustion during bulk operations.
    • Lazy-load job arguments on-demand for improved efficiency.
  • Chores

    • Version bumped to 0.4.0.
    • Note: Backwards compatibility requires version 0.3.2+ or clearing existing work items with payloads before downgrading.

@coderabbitai
Copy link

coderabbitai bot commented Feb 17, 2026

No actionable comments were generated in the recent review. 🎉


ℹ️ Recent review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e88c077 and 2ed6875.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • CHANGELOG.md
  • package.json
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md

📝 Walkthrough

Walkthrough

This PR introduces size-aware batching and payload storage mechanisms across the work queue system. Large function arguments and completion context exceeding 8KB are stored in separate payload documents with references in work items. Enqueue and completion operations batch items by encoded size. Workers lazily load large arguments from payloads. Cursor-based pagination is replaced with iterative loops using size guards.

Changes

Cohort / File(s) Summary
Client-side Batching
src/client/index.ts
Implements batching logic in enqueueBatch to split large fnArgs sets into multiple batches using MAX_BATCH_SIZE, measuring each item via getConvexSize, processing each batch as separate mutations, and aggregating returned WorkIds.
Payload Storage and Thresholds
src/component/lib.ts
Introduces size checks for function arguments and context using getConvexSize. Stores large args/context in separate payload documents when exceeding INLINE_METADATA_THRESHOLD (8KB), with MAX_DOC_SIZE (1MB) enforcement. Refactors work item construction to use WithoutSystemFields pattern.
Size-Aware Completion Batching
src/component/complete.ts
Adds empty job validation, work-job pair validation, size-aware batching for completion jobs, schedules subsequent batches via scheduler. Implements robust context retrieval from onComplete or fallback payloads. Adds payload cleanup after completion and pending completion enqueuing.
Iterative Cleanup with Size Guards
src/component/danger.ts
Replaces cursor-based pagination with manual iterative loops. Introduces MAX_ROWS_READ and MAX_BYTES_READ guards using getConvexSize. Enhances data cleanup to delete related payloads and work records. Removes cursor parameter from public API for clearPending and clearOldWork.
Worker Payload Retrieval
src/component/worker.ts, src/component/loop.ts
Adds getWorkArgs query handler for lazy loading arguments from payloads. Updates runMutationWrapper and runActionWrapper to accept optional payloadId and fnArgs, fetching from payload when fnArgs absent. Propagates payloadId through work scheduling in loop handler.
Recovery and Type Updates
src/component/recovery.ts
Replaces toComplete accumulation with completionJobs using new CompleteJob type. Updates imports to use type CompleteJob instead of completeArgs schema.
Test and Version Updates
example/convex/test/work.ts, package.json, CHANGELOG.md
Removes unused internal import in test. Bumps version to 0.4.0. Documents storage/batching changes and backwards-compatibility requirements in changelog.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Enqueue as Enqueue Handler
    participant DB as Database
    participant Worker as Worker
    participant Complete as Complete Handler

    rect rgba(100, 150, 200, 0.5)
        Note over Client,DB: Large Args Storage Path
        Client->>Enqueue: enqueue({ args: large_payload })
        Enqueue->>Enqueue: Check size with getConvexSize
        alt args + context exceed INLINE_METADATA_THRESHOLD
            Enqueue->>DB: INSERT payload { args, context }
            DB-->>Enqueue: payloadId
            Enqueue->>DB: INSERT work { payloadId, fnHandle }
        else fits inline
            Enqueue->>DB: INSERT work { fnArgs, context_inline }
        end
        DB-->>Enqueue: workId
        Enqueue-->>Client: workId
    end

    rect rgba(100, 200, 150, 0.5)
        Note over Worker,Complete: Lazy Loading Execution Path
        Worker->>DB: fetch work by workId
        alt work.payloadId exists
            Worker->>DB: getWorkArgs(payloadId)
            DB-->>Worker: { args, context }
        else inline args
            Worker->>Worker: use work.fnArgs directly
        end
        Worker->>Worker: execute function with args
        Worker->>Complete: schedule completion with result
    end

    rect rgba(200, 150, 100, 0.5)
        Note over Complete,DB: Batched Completion Path
        Complete->>Complete: collect jobs, batch by size
        loop For each batch
            Complete->>DB: update work, record completion
            alt large context in payload
                Complete->>DB: delete payload
            end
            Complete->>DB: delete work record
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Ian/optional config #150: Modifies enqueue/enqueueBatch and related config handling in client/lib with batch/arg processing changes.

Poem

🐰 Batches bundled, payloads stashed,
Large arguments safely cached,
When workers call, we fetch with care,
Lazy loading through the air!
No more cursors, loops instead—
Size-aware work, by bounds well-led. 🎯

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: introducing separate payload storage for large function arguments and context data to avoid work document size limits.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ian/handle-big-args

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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 and usage tips.

Copy link
Member Author

ianmacartney commented Feb 17, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 17, 2026

Open in StackBlitz

npm i https://pkg.pr.new/get-convex/workpool/@convex-dev/workpool@170

commit: 2ed6875

@ianmacartney ianmacartney changed the title start storing args separately store args separately Feb 17, 2026
@ianmacartney ianmacartney changed the title store args separately store args/context separately from Feb 17, 2026
@ianmacartney ianmacartney changed the title store args/context separately from store args/context separately from work when large Feb 17, 2026
@ianmacartney ianmacartney marked this pull request as ready for review February 17, 2026 11:08
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/component/worker.ts (1)

108-108: Minor: Comment incorrectly states "mutation" but this is a query.

📝 Proposed fix
-// Helper mutation for actions to fetch work args
+// Helper query for actions to fetch work args
 export const getWorkArgs = internalQuery({
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/component/worker.ts` at line 108, The inline comment "Helper mutation for
actions to fetch work args" is incorrect; update the comment above the helper to
indicate it's a query (e.g., "Helper query for actions to fetch work args") so
the comment matches the implementation; locate the comment text "Helper mutation
for actions to fetch work args" in src/component/worker.ts and replace
"mutation" with "query" to keep semantics and documentation consistent.
src/component/danger.ts (1)

110-119: Debug log may print undefined for fnArgs when stored in payload.

When args are offloaded to the payload table, entry.fnArgs will be undefined. This could make debug logs less useful.

📝 Proposed improvement
       console.debug(
-        `cleared ${entry.fnName}: ${entry.fnArgs} (${Object.entries({
+        `cleared ${entry.fnName}${entry.payloadId ? ' (args in payload)' : `: ${JSON.stringify(entry.fnArgs)}`} (${Object.entries({
           pendingStart,
           pendingCompletion,
           pendingCancelation,
         })
           .filter(([_, v]) => v !== null)
           .map(([name]) => name)
           .join(", ")})`,
       );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/component/danger.ts` around lines 110 - 119, The debug message currently
interpolates entry.fnArgs which can be undefined when args are offloaded to
payload; update the console.debug call in this block to compute a safe
readableArgs (e.g., use entry.fnArgs if present, otherwise extract and
JSON.stringify a fallback from entry.payload, and guard stringification to avoid
throwing) and use readableArgs in the template; reference the existing symbols
entry.fnArgs, entry.payload, console.debug and the surrounding
pendingStart/pendingCompletion/pendingCancelation mapping when making the
change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/component/danger.ts`:
- Around line 36-43: The calls to ctx.db.delete in the block handling work are
using two-argument forms (e.g., ctx.db.delete("payload", work.payloadId) and
ctx.db.delete("work", work._id)) but the API accepts a single document ID;
change these to pass only the ID values (use work.payloadId and work._id
respectively), keeping the existing ctx.db.delete(entry._id) call as-is so
deletion uses the correct single-argument API.

---

Nitpick comments:
In `@src/component/danger.ts`:
- Around line 110-119: The debug message currently interpolates entry.fnArgs
which can be undefined when args are offloaded to payload; update the
console.debug call in this block to compute a safe readableArgs (e.g., use
entry.fnArgs if present, otherwise extract and JSON.stringify a fallback from
entry.payload, and guard stringification to avoid throwing) and use readableArgs
in the template; reference the existing symbols entry.fnArgs, entry.payload,
console.debug and the surrounding
pendingStart/pendingCompletion/pendingCancelation mapping when making the
change.

In `@src/component/worker.ts`:
- Line 108: The inline comment "Helper mutation for actions to fetch work args"
is incorrect; update the comment above the helper to indicate it's a query
(e.g., "Helper query for actions to fetch work args") so the comment matches the
implementation; locate the comment text "Helper mutation for actions to fetch
work args" in src/component/worker.ts and replace "mutation" with "query" to
keep semantics and documentation consistent.

Comment on lines +36 to +43
await ctx.db.delete(entry._id);
if (work) {
// Clean up any large data stored separately
if (work.payloadId) {
await ctx.db.delete("payload", work.payloadId);
}
}),
);
if (!entries.isDone) {
await ctx.db.delete("work", work._id);
}
Copy link

@coderabbitai coderabbitai bot Feb 17, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's find the file and check the exact content at lines 36-43
head -50 src/component/danger.ts | tail -20

Repository: get-convex/workpool

Length of output: 707


🏁 Script executed:

# Find all ctx.db.delete usages to identify the correct API pattern
rg -n "ctx\.db\.delete\(" --type ts -B2 -A2 | head -60

Repository: get-convex/workpool

Length of output: 3317


🏁 Script executed:

# Look for type definitions or implementations of ctx.db.delete
fd -e ts -e tsx | xargs grep -l "\.db\.delete" | head -10

Repository: get-convex/workpool

Length of output: 322


🏁 Script executed:

# Search for Convex db type definitions or documentation
find . -name "*.ts" -o -name "*.d.ts" | xargs grep -l "delete.*payloadId\|delete.*work\._id" 2>/dev/null | head -5

Repository: get-convex/workpool

Length of output: 116


🏁 Script executed:

# Check complete.ts for ctx.db.delete usage patterns
rg -n "ctx\.db\.delete\(" src/component/complete.ts -B2 -A2

Repository: get-convex/workpool

Length of output: 407


🏁 Script executed:

# Look at the actual type definition or import of db from ctx
rg -n "ctx\.db" src/component/danger.ts | head -20

Repository: get-convex/workpool

Length of output: 743


🏁 Script executed:

# Check if there are any Convex library files that define the db.delete signature
find . -name "*.d.ts" -o -name "convex*" -type f | xargs grep -l "delete" 2>/dev/null | head -10

Repository: get-convex/workpool

Length of output: 86


🏁 Script executed:

# Get more context on the complete.ts deletions to confirm the pattern
rg -n "ctx\.db\.delete\(" src/component/complete.ts -B3 -A1

Repository: get-convex/workpool

Length of output: 422


🏁 Script executed:

# Also check danger.ts for all delete calls to see the inconsistency
cat -n src/component/danger.ts | sed -n '35,125p'

Repository: get-convex/workpool

Length of output: 3564


Incorrect ctx.db.delete API usage at lines 40 and 42 — will cause runtime errors.

The ctx.db.delete method takes a single argument (the document ID). Lines 40 and 42 incorrectly pass two arguments with a table name prefix, which contradicts the correct single-argument usage used throughout the rest of the codebase.

🐛 Proposed fix
      await ctx.db.delete(entry._id);
      if (work) {
        // Clean up any large data stored separately
        if (work.payloadId) {
-          await ctx.db.delete("payload", work.payloadId);
+          await ctx.db.delete(work.payloadId);
        }
-        await ctx.db.delete("work", work._id);
+        await ctx.db.delete(work._id);
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await ctx.db.delete(entry._id);
if (work) {
// Clean up any large data stored separately
if (work.payloadId) {
await ctx.db.delete("payload", work.payloadId);
}
}),
);
if (!entries.isDone) {
await ctx.db.delete("work", work._id);
}
await ctx.db.delete(entry._id);
if (work) {
// Clean up any large data stored separately
if (work.payloadId) {
await ctx.db.delete(work.payloadId);
}
await ctx.db.delete(work._id);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/component/danger.ts` around lines 36 - 43, The calls to ctx.db.delete in
the block handling work are using two-argument forms (e.g.,
ctx.db.delete("payload", work.payloadId) and ctx.db.delete("work", work._id))
but the API accepts a single document ID; change these to pass only the ID
values (use work.payloadId and work._id respectively), keeping the existing
ctx.db.delete(entry._id) call as-is so deletion uses the correct single-argument
API.

Copy link
Member Author

Choose a reason for hiding this comment

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

convex now allows for both syntax

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

🌐 Web query:

Convex ctx.db.delete two argument syntax table name

💡 Result:

In Convex (since convex v1.31.0), ctx.db.delete supports the two-argument form where the table name comes first:

await ctx.db.delete("messages", messageId);
  • New (recommended): ctx.db.delete(tableName, id) (table name is currently optional but encouraged) [1][2]
  • Old (still supported for now): ctx.db.delete(id) (table inferred from the ID encoding) [1][2]

Sources: [1] [2]


@ianmacartney Thank you for the correction! You're absolutely right. Since Convex v1.31.0, ctx.db.delete supports the two-argument form ctx.db.delete(tableName, id), which is actually the newer recommended syntax, while the single-argument form remains supported for backward compatibility.

I apologize for the incorrect review comment. The code in danger.ts is using the correct, modern API. I've noted this for future reviews.


✏️ Learnings added
Learnt from: ianmacartney
Repo: get-convex/workpool PR: 170
File: src/component/danger.ts:36-43
Timestamp: 2026-02-19T08:37:48.834Z
Learning: In Convex (since v1.31.0), `ctx.db.delete` supports both the two-argument form `ctx.db.delete(tableName, id)` (newer, recommended) and the single-argument form `ctx.db.delete(id)` (older, still supported). Both are valid API usage.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: sethconvex
Repo: get-convex/workpool PR: 167
File: src/component/batch.ts:465-485
Timestamp: 2026-02-14T00:53:51.808Z
Learning: In the workpool BatchWorkpool implementation (src/component/batch.ts), the `cancel` mutation intentionally does not guard against cancelling claimed (actively running) tasks. This is safe by design: the executor's `complete`/`fail` calls handle missing tasks gracefully (early return on null), and adding a status check before deletion would introduce a TOCTOU race condition between the check and the delete. The delete-first approach with idempotent cleanup is the correct pattern.

Learnt from: sethconvex
Repo: get-convex/workpool PR: 167
File: src/component/pool.test.ts:0-0
Timestamp: 2026-02-14T00:53:37.586Z
Learning: In the workpool repository using convex-test, afterEach cleanup with `t.finishAllScheduledFunctions(vi.runAllTimers)` is not required in test files like `src/component/pool.test.ts` because convex-test creates isolated environments per test, making such cleanup purely cosmetic.

@ianmacartney ianmacartney changed the base branch from ian/load-test to graphite-base/170 February 19, 2026 01:52
@ianmacartney ianmacartney changed the base branch from graphite-base/170 to main February 19, 2026 01:52
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
example/convex/test/work.ts (1)

18-35: ⚠️ Potential issue | 🟡 Minor

generateData double-counts the remainder, producing strings longer than len.

Lines 22-24 already append a partial chunk for len % 10. Lines 30-33 then append the same remainder again, so the result is len + (len % 10) characters instead of len.

🐛 Proposed fix: remove the duplicate remainder block
   let result = chunks.join("");
-  // Add remaining bytes for exact length
-  const remaining = len % 10;
-  if (remaining > 0) {
-    result += chunk.substring(0, remaining);
-  }
   return result;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@example/convex/test/work.ts` around lines 18 - 35, The generateData function
appends the partial remainder twice, causing output longer than len; remove the
second remainder append (the block that computes remaining and appends
chunk.substring(0, remaining)) and rely only on the earlier conditional that
pushes the partial chunk into the chunks array, ensuring the final result =
chunks.join("") has exactly len characters; check references to chunk,
numChunks, remaining, and chunks to validate the duplicate logic is removed.
🧹 Nitpick comments (3)
src/component/danger.ts (1)

110-119: Debug log may serialize large fnArgs for deleted work items.

Line 111 interpolates entry.fnArgs into the log string. With the new payload externalization, fnArgs on the work document could be undefined (displaying as "undefined"), which is fine. But when args are inlined (small args), the entire args object is stringified. For a cleanup operation that can process many rows, this may generate a lot of log output.

Consider using entry.fnName only, or truncating.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/component/danger.ts` around lines 110 - 119, The debug log in the
console.debug call currently interpolates entry.fnArgs which may stringify large
payloads; update the log in the clearing/cleanup routine to avoid serializing
full args — reference the existing console.debug and the entry object
(entry.fnName, entry.fnArgs) and replace the interpolation of entry.fnArgs with
either only entry.fnName or a truncated/summary form (e.g.
JSON.stringify(entry.fnArgs) sliced to a short max length or a flag like
"<omitted>") so the cleanup loop doesn't emit massive logs; keep the existing
pendingStart/pendingCompletion/pendingCancelation name extraction intact.
src/component/lib.ts (1)

86-111: Shallow spread silently shares onComplete reference — safe here but fragile.

workItem = { ...workArgs, attempts: 0 } shallow-copies, so workItem.onComplete is the same object as workArgs.onComplete. The delete workItem.onComplete!.context on lines 102 and 109 mutates the caller's object too. This is safe today because workArgs isn't read after this point, but it's a subtle coupling that could bite during future refactoring.

♻️ Optional: deep-copy onComplete before mutating
   const workItem: WithoutSystemFields<Doc<"work">> = {
     ...workArgs,
+    onComplete: workArgs.onComplete ? { ...workArgs.onComplete } : undefined,
     attempts: 0,
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/component/lib.ts` around lines 86 - 111, The shallow spread creating
workItem copies a reference to workArgs.onComplete so subsequent delete
workItem.onComplete!.context mutations also modify the caller; to fix, ensure
workItem gets its own copy of onComplete (e.g., when constructing workItem or
immediately after, set workItem.onComplete = workArgs.onComplete ? {
...workArgs.onComplete } : undefined) so the later delete
workItem.onComplete!.context calls do not mutate workArgs; apply this change
before any code paths that call delete workItem.onComplete!.context (references:
workItem, workArgs, onComplete, delete workItem.onComplete!.context).
src/component/worker.ts (1)

34-36: Standardize ctx.db.get to two-argument form and strengthen assertion narrowing

Three issues in this block:

  1. Deprecated ctx.db.get form: Line 34 uses the single-argument ctx.db.get(args.payloadId), while line 115 in the same file correctly uses the modern two-argument form ctx.db.get("payload", args.payloadId). Since Convex v1.31.0, the two-argument form with explicit table name is the recommended API. Update line 34 to match.

  2. Implicit assertion narrowing: assert(payload?.args) at line 35 asserts the result of an optional-chain expression, which may not reliably narrow payload itself to exclude null. Line 36 then accesses payload.args. The pattern in getWorkArgs (lines 116–117) is clearer: separate assertions for payload and payload.args provide explicit narrowing and clearer failure attribution.

  3. Incorrect comment: Line 108 labels getWorkArgs as "Helper mutation for actions" but it is an internalQuery, not a mutation.

🛠️ Proposed fix
-    const payload = await ctx.db.get(args.payloadId);
-    assert(payload?.args);
-    fnArgs = payload.args;
+    const payload = await ctx.db.get("payload", args.payloadId);
+    assert(payload);
+    assert(payload.args);
+    fnArgs = payload.args;
-// Helper mutation for actions to fetch work args
+// Helper query for actions to fetch work args
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/component/worker.ts` around lines 34 - 36, Update the single-argument db
call to the two-argument form and strengthen assertion narrowing: replace
ctx.db.get(args.payloadId) with ctx.db.get("payload", args.payloadId) inside the
worker code, then assert the payload itself (assert(payload)) followed by
asserting its property (assert(payload.args)) before using payload.args to
ensure proper type narrowing; also fix the misleading comment above getWorkArgs
to label it as an internalQuery (not a mutation).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/component/worker.ts`:
- Around line 108-109: The comment above getWorkArgs incorrectly calls it a
"mutation"; update the comment to say "Helper query for actions to fetch work
args" (or similar) to match that getWorkArgs is exported via internalQuery;
locate the comment that begins "Helper mutation for actions to fetch work args"
and change "mutation" to "query" to keep wording consistent with the exported
internalQuery.

---

Outside diff comments:
In `@example/convex/test/work.ts`:
- Around line 18-35: The generateData function appends the partial remainder
twice, causing output longer than len; remove the second remainder append (the
block that computes remaining and appends chunk.substring(0, remaining)) and
rely only on the earlier conditional that pushes the partial chunk into the
chunks array, ensuring the final result = chunks.join("") has exactly len
characters; check references to chunk, numChunks, remaining, and chunks to
validate the duplicate logic is removed.

---

Duplicate comments:
In `@src/component/danger.ts`:
- Around line 36-43: The clearPending function mixes ctx.db.delete forms
(single-arg and two-arg), which reduces readability; update the two-argument
calls in clearPending (ctx.db.delete("payload", work.payloadId) and
ctx.db.delete("work", work._id)) to the single-argument form used in
clearOldWork by passing the full document id value (e.g., payloadId and work._id
directly) so all ctx.db.delete calls in this file use the single-argument style
for consistency.

---

Nitpick comments:
In `@src/component/danger.ts`:
- Around line 110-119: The debug log in the console.debug call currently
interpolates entry.fnArgs which may stringify large payloads; update the log in
the clearing/cleanup routine to avoid serializing full args — reference the
existing console.debug and the entry object (entry.fnName, entry.fnArgs) and
replace the interpolation of entry.fnArgs with either only entry.fnName or a
truncated/summary form (e.g. JSON.stringify(entry.fnArgs) sliced to a short max
length or a flag like "<omitted>") so the cleanup loop doesn't emit massive
logs; keep the existing pendingStart/pendingCompletion/pendingCancelation name
extraction intact.

In `@src/component/lib.ts`:
- Around line 86-111: The shallow spread creating workItem copies a reference to
workArgs.onComplete so subsequent delete workItem.onComplete!.context mutations
also modify the caller; to fix, ensure workItem gets its own copy of onComplete
(e.g., when constructing workItem or immediately after, set workItem.onComplete
= workArgs.onComplete ? { ...workArgs.onComplete } : undefined) so the later
delete workItem.onComplete!.context calls do not mutate workArgs; apply this
change before any code paths that call delete workItem.onComplete!.context
(references: workItem, workArgs, onComplete, delete
workItem.onComplete!.context).

In `@src/component/worker.ts`:
- Around line 34-36: Update the single-argument db call to the two-argument form
and strengthen assertion narrowing: replace ctx.db.get(args.payloadId) with
ctx.db.get("payload", args.payloadId) inside the worker code, then assert the
payload itself (assert(payload)) followed by asserting its property
(assert(payload.args)) before using payload.args to ensure proper type
narrowing; also fix the misleading comment above getWorkArgs to label it as an
internalQuery (not a mutation).

Comment on lines +108 to +109
// Helper mutation for actions to fetch work args
export const getWorkArgs = internalQuery({
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Incorrect comment: "Helper mutation" should be "Helper query"

The comment on line 108 describes getWorkArgs as a "mutation," but it is exported as internalQuery.

✏️ Proposed fix
-// Helper mutation for actions to fetch work args
+// Helper query for actions to fetch work args
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Helper mutation for actions to fetch work args
export const getWorkArgs = internalQuery({
// Helper query for actions to fetch work args
export const getWorkArgs = internalQuery({
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/component/worker.ts` around lines 108 - 109, The comment above
getWorkArgs incorrectly calls it a "mutation"; update the comment to say "Helper
query for actions to fetch work args" (or similar) to match that getWorkArgs is
exported via internalQuery; locate the comment that begins "Helper mutation for
actions to fetch work args" and change "mutation" to "query" to keep wording
consistent with the exported internalQuery.

@ianmacartney ianmacartney merged commit 2ed6875 into main Feb 23, 2026
4 checks passed
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