Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Never use Date.now() in queries as it breaks caching and reactivity
.claude/skills/kunanonj-cursor-plugin-convex-rule-no-date-now-in-queries/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 6% | 0% |
| case-09 | ✗→✓ | ▲ Improved | -18% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 41% | 0% |
| case-11 | ✗→✓ | ▲ Improved | -50% | 0% |
| case-13 | ✗→✓ | ▲ Improved | -7% | 0% |
Never use Date.now() or new Date() inside query functions. It prevents proper caching and breaks reactive subscriptions.
Queries should be deterministic. Using Date.now() means the query returns different results every millisecond, defeating Convex's reactivity system.
typescriptexport const getActiveTasks = query({ handler: async (ctx) => { const now = Date.now(); // ❌ Don't do this return await ctx.db .query("tasks") .filter(q => q.lt(q.field("dueDate"), now)) .collect(); }, });
typescriptexport const getActiveTasks = query({ args: { now: v.number() }, handler: async (ctx, args) => { return await ctx.db .query("tasks") .filter(q => q.lt(q.field("dueDate"), args.now)) .collect(); }, }); // Client passes current time const tasks = useQuery(api.tasks.getActiveTasks, { now: Date.now() });
typescript// Update status periodically with a cron job export const updateTaskStatuses = internalMutation({ handler: async (ctx) => { const now = Date.now(); const expiredTasks = await ctx.db .query("tasks") .withIndex("by_status", q => q.eq("status", "active")) .filter(q => q.lt(q.field("dueDate"), now)) .collect(); for (const task of expiredTasks) { await ctx.db.patch(task._id, { status: "expired" }); } }, }); // Query is simple and efficient export const getActiveTasks = query({ handler: async (ctx) => { return await ctx.db .query("tasks") .withIndex("by_status", q => q.eq("status", "active")) .collect(); }, });
If you need day-level filtering:
typescriptexport const getToday = query({ args: { today: v.string() }, // "2024-01-15" handler: async (ctx, args) => { return await ctx.db .query("events") .withIndex("by_date", q => q.eq("date", args.today)) .collect(); }, });
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | pass→pass | 10,527 | 8,715 | -17% | 1 | 1 | 0% | 2,317 | 2,754 | +19% | 0 | 0 | — |
case-02 | fail→pass | 7,525 | 4,410 | -41% | 1 | 1 | 0% | 1,565 | 1,652 | +6% | 0 | 0 | — |
case-03 | pass→pass | 9,721 | 4,961 | -49% | 1 | 1 | 0% | 2,204 | 1,734 | -21% | 0 | 0 | — |
case-04 | pass→pass | 7,589 | 5,464 | -28% | 1 | 1 | 0% | 1,582 | 1,623 | +3% | 0 | 0 | — |
case-05 | pass→pass | 9,662 | 4,799 | -50% | 1 | 1 | 0% | 2,158 | 1,754 | -19% | 0 | 0 | — |
case-06 | pass→pass | 7,386 | 4,529 | -39% | 1 | 1 | 0% | 1,581 | 1,444 | -9% | 0 | 0 | — |
case-07 | pass→pass | 9,285 | 7,078 | -24% | 1 | 1 | 0% | 1,913 | 1,561 | -18% | 0 | 0 | — |
case-08 | pass→pass | 8,458 | 3,568 | -58% | 1 | 1 | 0% | 1,874 | 1,389 | -26% | 0 | 0 | — |
case-09 | fail→pass | 15,605 | 10,615 | -32% | 1 | 1 | 0% | 3,497 | 2,861 | -18% | 0 | 0 | — |
case-10 | fail→pass | 6,457 | 5,594 | -13% | 1 | 1 | 0% | 1,353 | 1,907 | +41% | 0 | 0 | — |
case-11 | fail→pass | 19,934 | 6,195 | -69% | 1 | 1 | 0% | 3,927 | 1,967 | -50% | 0 | 0 | — |
case-12 | pass→fail | 8,472 | 5,776 | -32% | 1 | 1 | 0% | 1,759 | 1,847 | +5% | 0 | 0 | — |
case-13 | fail→pass | 10,103 | 6,688 | -34% | 1 | 1 | 0% | 2,409 | 2,244 | -7% | 0 | 0 | — |
case-14 | pass→pass | 7,383 | 4,852 | -34% | 1 | 1 | 0% | 1,479 | 1,705 | +15% | 0 | 0 | — |
case-15 | pass→pass | 9,081 | 4,897 | -46% | 1 | 1 | 0% | 1,862 | 1,734 | -7% | 0 | 0 | — |
case-16 | fail→pass | 6,568 | 4,259 | -35% | 1 | 1 | 0% | 1,380 | 1,583 | +15% | 0 | 0 | — |
case-17 | pass→pass | 11,783 | 4,216 | -64% | 1 | 1 | 0% | 2,524 | 1,610 | -36% | 0 | 0 | — |
case-18 | pass→pass | 12,690 | 6,852 | -46% | 1 | 1 | 0% | 2,639 | 2,147 | -19% | 0 | 0 | — |
case-19 | pass→pass | 14,088 | 5,524 | -61% | 1 | 1 | 0% | 3,056 | 1,854 | -39% | 0 | 0 | — |
case-20 | fail→pass | 8,154 | 4,830 | -41% | 1 | 1 | 0% | 2,089 | 1,881 | -10% | 0 | 0 | — |
case-21 | pass→pass | 14,500 | 6,632 | -54% | 1 | 1 | 0% | 3,058 | 2,126 | -30% | 0 | 0 | — |
case-22 | pass→pass | 9,713 | 7,024 | -28% | 1 | 1 | 0% | 2,096 | 1,934 | -8% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +27 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.