Skip to content

Allow calling migration mutations directly#47

Merged
ianmacartney merged 10 commits into
mainfrom
ian/call-directly
Apr 22, 2026
Merged

Allow calling migration mutations directly#47
ianmacartney merged 10 commits into
mainfrom
ian/call-directly

Conversation

@ianmacartney
Copy link
Copy Markdown
Member

@ianmacartney ianmacartney commented Apr 16, 2026

Allow calling a migration directly

Enhanced migration system to support direct CLI invocation:

  • Migrations can now be called directly: npx convex run migrations:myMigration
  • Auto-detects direct invocation vs scheduled batch execution
  • Maintains backward compatibility with migrations.runner() pattern
  • Added function name validation for security
  • Improved error messages and usage hints

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Convex AI guidance routing skill with documentation
    • Introduced new utilities for function metadata and transaction metrics retrieval
    • Enabled auto-detection of CLI/dashboard migration invocation
  • Documentation

    • Updated README with revised CLI examples for migration execution
  • Refactor

    • Simplified migration runner pattern; removed prefix-based alternatives
  • Chores

    • Updated development dependencies

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

📝 Walkthrough

Walkthrough

A new convex routing skill is introduced with documentation and lock entry; migration CLI invocation is simplified with direct command examples replacing function-based runners; example code simplified by removing prefix variants; client-side logic enhanced to auto-detect CLI/dashboard invocation and fetch function metadata; development dependency version updated.

Changes

Cohort / File(s) Summary
Convex Routing Skill
.agents/skills/convex/SKILL.md, .claude/skills/convex
New routing skill documentation and reference added to guide users to relevant Convex sub-skills based on intent.
CLI Migration Examples
README.md
Updated examples to use direct npx convex run migrations:<name> invocation syntax; removed one-off runner documentation; clarified reset and dryRun behavior.
Example Simplification
example/convex/example.ts
Removed migrationsWithPrefix and runWithPrefix exports, eliminating the alternative example: prefix runner entry point.
Dependencies & Locks
package.json, skills-lock.json
Bumped convex-test dev dependency from 0.0.41 to 0.0.48; added convex skill lock entry from get-convex/agent-skills.
Client Logic & Utilities
src/client/index.ts
Refactored migration handler to auto-detect CLI/dashboard invocation based on argument presence; added getFunctionMetadata() and getTransactionMetrics() utilities; validates function metadata against provided function name with detailed error hints.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Ian/reset #15: Modifies migration control flow to add and wire reset-style behavior, including similar README examples and cursor/reset handling changes in src/client/index.ts.

Poem

🐰 Through migration paths, a rabbit now routes,
With skills to guide all CLI calls,
Metadata checked and functions corrected,
Simple commands bloom where complexity falls! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: enabling direct CLI invocation of migration mutations (e.g., 'npx convex run migrations:myMigration') rather than requiring a runner wrapper.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ 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 ian/call-directly

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
Copy Markdown
Member Author

ianmacartney commented Apr 16, 2026

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

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 16, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@convex-dev/migrations@47

commit: d4e2db0

@ianmacartney ianmacartney changed the title add stubs for syscalls Add Convex agent skills and direct migration execution Apr 16, 2026
@ianmacartney ianmacartney changed the title Add Convex agent skills and direct migration execution Allow calling migration mutations directly Apr 16, 2026
@ianmacartney ianmacartney reopened this Apr 16, 2026
@ianmacartney ianmacartney requested a review from reeceyang April 20, 2026 22:33
@ianmacartney ianmacartney marked this pull request as ready for review April 20, 2026 22:33
Copy link
Copy Markdown

@reeceyang reeceyang left a comment

Choose a reason for hiding this comment

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

cool!

Comment thread src/client/index.ts Outdated
const metadata = await getFunctionMetadata();
if (args.fn && this.prefixedName(args.fn) !== metadata.name) {
throw new Error(
`Function name mismatch: expected ${metadata.name}, got ${args.fn}. Hint: you can `,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

looks like this hint might be cut off?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

good catch! thanks

Copy link
Copy Markdown

@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)
.claude/skills/convex (1)

1-1: Consider adding a trailing newline.

POSIX convention recommends that text files end with a newline character. While not critical, adding one improves compatibility with certain tools and editors.

📝 Proposed fix
-../../.agents/skills/convex
\ No newline at end of file
+../../.agents/skills/convex
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/convex at line 1, The file named "convex" in the skills set
is missing a trailing newline; open that file and append a single POSIX newline
character (LF) at the end so the file ends with '\n' (ensure your editor uses
Unix line endings), save and commit the change.
src/client/index.ts (1)

753-754: Fix the copied TODO target.

This helper is for transaction metrics, but the TODO says to replace it with ctx.meta.getFunctionMetadata(). That will send maintainers to the wrong Convex API when the syscall shim is removed.

Proposed wording fix
-// TODO: replace with ctx.meta.getFunctionMetadata() in 1.36+
+// TODO: replace with ctx.meta.getTransactionMetrics() in 1.36+
 export async function getTransactionMetrics(): Promise<TransactionMetrics> {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/index.ts` around lines 753 - 754, The TODO above the
getTransactionMetrics function references the wrong Convex API
(ctx.meta.getFunctionMetadata()); update the comment to point to the correct API
for transaction metrics (e.g., ctx.meta.getTransactionMetrics() or the accurate
future syscall name) so maintainers are directed to the proper replacement when
the syscall shim is removed; change the TODO text near the getTransactionMetrics
declaration to mention the correct ctx.meta.* method for transaction metrics.
🤖 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/client/index.ts`:
- Around line 294-330: Harden the direct-invocation detection in the block using
args (check for args.fn, args.next, args.cursor, args.dryRun, args.batchSize) so
null is treated as an invalid/ambiguous sentinel: update the condition that
decides direct vs batch invocation to consider args.cursor === null (and/or
args.dryRun === null) as triggering the direct-invocation branch or immediately
throw a clear error instructing callers to use reset:true or proper dryRun
values; specifically modify the logic around the existing check and the
subsequent cursor validation before batch processing (the code that calls
getFunctionMetadata(), this.prefixedName(args.fn) check, and the call to
this._runInteractive with makeFunctionReference) to either accept null as
direct-invocation or to reject ambiguous combos like {cursor:null,dryRun:false}
with a user-facing Error guiding them to use reset:true.

---

Nitpick comments:
In @.claude/skills/convex:
- Line 1: The file named "convex" in the skills set is missing a trailing
newline; open that file and append a single POSIX newline character (LF) at the
end so the file ends with '\n' (ensure your editor uses Unix line endings), save
and commit the change.

In `@src/client/index.ts`:
- Around line 753-754: The TODO above the getTransactionMetrics function
references the wrong Convex API (ctx.meta.getFunctionMetadata()); update the
comment to point to the correct API for transaction metrics (e.g.,
ctx.meta.getTransactionMetrics() or the accurate future syscall name) so
maintainers are directed to the proper replacement when the syscall shim is
removed; change the TODO text near the getTransactionMetrics declaration to
mention the correct ctx.meta.* method for transaction metrics.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: e57a2296-8b2f-4d1b-a843-d7f9ad296c63

📥 Commits

Reviewing files that changed from the base of the PR and between 38df1b3 and d4e2db0.

⛔ Files ignored due to path filters (3)
  • example/convex/_generated/ai/ai-files.state.json is excluded by !**/_generated/**
  • example/convex/_generated/ai/guidelines.md is excluded by !**/_generated/**
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • .agents/skills/convex/SKILL.md
  • .claude/skills/convex
  • README.md
  • example/convex/example.ts
  • package.json
  • skills-lock.json
  • src/client/index.ts
💤 Files with no reviewable changes (1)
  • example/convex/example.ts

Comment thread src/client/index.ts
Comment on lines +294 to +330
// Auto-detect function name for direct CLI/dashboard invocation.
// The component always provides cursor and dryRun when scheduling
// batches, so if either is missing this is a direct invocation.
if (
args.fn ||
args.next ||
args.cursor === undefined ||
args.cursor === "" ||
args.dryRun === undefined ||
args.batchSize === 0
) {
console.warn(
"Running this from the CLI or dashboard? Here's some args to use:",
);
console.warn({
"Dry run": '{ "dryRun": true, "reset": true }',
"For real": '{ "fn": "path/to/migrations:yourFnName" }',
});
if (args.cursor === "" || args.batchSize === 0) {
console.warn(
"Running this from the CLI or dashboard? Here's some args to use:",
);
console.warn({
"Dry run": '{ "dryRun": true, "reset": true }',
"For real": "{}",
});
return;
}
const metadata = await getFunctionMetadata();
if (args.fn && this.prefixedName(args.fn) !== metadata.name) {
const componentFlag = metadata.componentPath
? ` --component ${metadata.componentPath}`
: "";
throw new Error(
`Function name mismatch: expected ${metadata.name}, got ${args.fn}. Hint: you can call it directly with \`npx convex run ${metadata.name}${componentFlag}\`.`,
);
}
return (await this._runInteractive(
ctx,
args,
makeFunctionReference(
metadata.name,
) as unknown as MigrationFunctionReference,
)) as any;
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 21, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect migration argument definitions and component scheduling calls
# to confirm whether scheduled worker invocations can be distinguished from direct CLI calls.

set -euo pipefail

echo "migrationArgs definition:"
rg -n -C4 'migrationArgs\s*='

echo
echo "Calls that pass cursor/dryRun/batchSize into migration function handles:"
rg -n -C5 '\b(cursor|dryRun|batchSize|fnHandle|oneBatchOnly)\b' --glob '!README.md'

Repository: get-convex/migrations

Length of output: 50379


🏁 Script executed:

#!/bin/bash
# Verify: can the internal mutation be called directly, and does component validation gate the risk?

echo "=== Component fnHandle validation (defensive guard) ==="
sed -n '52,58p' src/component/lib.ts

echo
echo "=== Check if oneBatchOnly is always present in scheduled calls ==="
rg -n 'ctx.scheduler.runAfter.*api.lib.migrate' -A5 src/component/lib.ts

echo
echo "=== Verify _runInteractive always provides fnHandle for component call ==="
sed -n '193,201p' src/client/index.ts

echo
echo "=== Check if cursor and oneBatchOnly together discriminate scheduled from CLI ==="
rg -n 'oneBatchOnly' src/component/lib.ts | head -20

Repository: get-convex/migrations

Length of output: 1448


🏁 Script executed:

#!/bin/bash
# Verify error handling for the specific scenario: {cursor: null, dryRun: false}

echo "=== Lines 333-350: Batch processing path validation ==="
sed -n '333,350p' src/client/index.ts

Repository: get-convex/migrations

Length of output: 874


🏁 Script executed:

#!/bin/bash
# Final verification: are internal mutations called only via runner/runOne, or can they be called directly?

echo "=== How is the internal mutation typically invoked by users? ==="
rg -n 'migrations.define|migrations.runner|migrations.runOne|migrations.runSerially' -A2 example/ | head -30

echo
echo "=== Confirm that internal mutations are NOT exported/exposed to users directly ==="
rg -n 'export.*internal.*Migration' src/client/index.ts

echo
echo "=== Check if runner() wraps the internal mutation ==="
sed -n '73,125p' src/client/index.ts

Repository: get-convex/migrations

Length of output: 3875


The direct-invocation detection logic is incomplete and should be hardened.

A call with {cursor: null, dryRun: false} does not trigger the direct-invocation branch (line 297) because cursor is neither undefined nor "", and dryRun is defined. This falls through to batch-processing (line 333), where cursor validation at line 334 also skips—because null is neither undefined nor "". The code then processes locally without component migration tracking.

While the component's fnHandle validation (lib.ts line 52) provides a second-line defense against direct calls, the incomplete detection logic is fragile. Either:

  • Add an explicit scheduler-only sentinel argument (beyond the optional oneBatchOnly), or
  • Harden the cursor/dryRun validation at lines 333–350 to explicitly error on ambiguous combinations like {cursor: null, dryRun: false} with a message directing users to use reset: true instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/index.ts` around lines 294 - 330, Harden the direct-invocation
detection in the block using args (check for args.fn, args.next, args.cursor,
args.dryRun, args.batchSize) so null is treated as an invalid/ambiguous
sentinel: update the condition that decides direct vs batch invocation to
consider args.cursor === null (and/or args.dryRun === null) as triggering the
direct-invocation branch or immediately throw a clear error instructing callers
to use reset:true or proper dryRun values; specifically modify the logic around
the existing check and the subsequent cursor validation before batch processing
(the code that calls getFunctionMetadata(), this.prefixedName(args.fn) check,
and the call to this._runInteractive with makeFunctionReference) to either
accept null as direct-invocation or to reject ambiguous combos like
{cursor:null,dryRun:false} with a user-facing Error guiding them to use
reset:true.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

following up in #49

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@ianmacartney, understood — thanks for the follow-up pointer! I'll track the hardening of the direct-invocation detection logic (handling {cursor: null, dryRun: false} ambiguity) in #49.

Copy link
Copy Markdown
Member Author

ianmacartney commented Apr 22, 2026

Merge activity

  • Apr 22, 4:42 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Apr 22, 4:42 AM UTC: @ianmacartney merged this pull request with Graphite.

@ianmacartney ianmacartney merged commit e233964 into main Apr 22, 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.

2 participants