Battles / Coding
Probably GitHub Copilot
Cursor vs GitHub Copilot · Coding
GitHub Copilot came out in front, but not by enough for us to call it proven on a suite this size. Treat it as the way to bet, not as a settled result.
GitHub Copilot took 4 of the 5 tasks that had a clear winner (Cursor 1, GitHub Copilot 4). The judge could pick a winner on 5 of 18 tasks; on the other 13 it could not tell them apart. That is a lean, not a proven win — at this sample size we cannot rule out chance, so we are not calling it decisive.
It was also the faster and the cheaper of the two.
Show the full workings
The evidence
Suite-by-suite
blue = Cursor wins · grey = ties · white = GitHub Copilot wins (18 tasks per suite)
Round-by-round — all 18 tasks
GitHub CopilotDuration parserCoding · 6.7 v 9.2Response 2 followed the instructions perfectly, providing exactly three assert-based tests as requested. Response 1 provided more than three…
Write a Python function parse_duration(s) that converts strings like '1h30m', '45s', '2h', '90m', '1h2m3s' into total seconds. Raise ValueError on anything malformed. Include three assert-based tests, one of which covers a malformed input.
```python import re def parse_duration(s): """ Convert a duration string like '1h30m', '45s', '2h', '90m', '1h2m3s' into the total number of seconds it represents. The format must be: an optional hours part (<digits>h), followed by an optional minutes part (<digits>m), followed by an optional seconds part (<digits>s), in that order. At least one part must be present, and no extra characters are allowed. Raises: ValueError: if the string is empty, not a string, has parts in the wrong order, contains unknown units, has no valid parts, or otherwise doesn't match the format. """ if not isinstance(s, str) or s == "": raise ValueError(f"Invalid duration: {s!r}") match = re.fullmatch(r'(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?', s) if match is None: raise ValueError(f"Invalid duration: {s!r}") hours, minutes, seconds = match.groups() if hours is None and minutes is None and seconds is None: raise ValueError(f"Invalid duration: {s!r}") total = 0 if hours is not None: total += int(hours) * 3600 if minutes is not None: total += int(minutes) * 60 if seconds is not None: total += int(seconds) return total # ---- Tests ---- def test_parse_duration(): # Combined hours and minutes assert parse_duration('1h30m') == 5400 # 1*3600 + 30*60 # Only minutes, and only seconds forms assert parse_duration('90m') == 5400 assert parse_duration('45s') == 45 # All three units combined assert parse_duration('1h2m3s') == 1 * 3600 + 2 * 60 + 3 # Malformed input: wrong order of units should raise ValueError try: parse_duration('30m1h') assert False, "Expected ValueError for malformed input" except ValueError: pass # Malformed input: unknown unit should raise ValueError try: parse_duration('10x') assert False, "Expected ValueError for unknown unit" except ValueError: pass # Malformed input: empty string should raise ValueError try: parse_duration('') assert False, "Expected ValueError for empty string" except ValueError: pass if __name__ == '__main__': test_parse_duration() print("All tests passed.") ``` **Notes:** - The regex `(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?` enforces strict ordering (h before m before s) and disallows repeated or out-of-order units. - `re.fullmatch` ensures no stray characters exist anywhere in the string. - An explicit check rejects the case where none of the three groups matched (e.g., an empty string), since the regex alone would otherwise match a fully empty duration.
```python import re def parse_duration(s: str) -> int: """ Convert a duration string like '1h30m', '45s', '2h', '90m', '1h2m3s' into total seconds. Rules: - One or more chunks of: <integer><unit> - unit is one of: h, m, s - each unit may appear at most once - no extra characters/spaces allowed """ if not isinstance(s, str) or not s: raise ValueError("Duration must be a non-empty string") pattern = re.compile(r'(\d+)([hms])') pos = 0 total = 0 seen_units = set() unit_to_seconds = {'h': 3600, 'm': 60, 's': 1} for match in pattern.finditer(s): if match.start() != pos: # gap means malformed text between valid chunks raise ValueError(f"Malformed duration: {s!r}") value = int(match.group(1)) unit = match.group(2) if unit in seen_units: raise ValueError(f"Duplicate unit {unit!r} in duration: {s!r}") seen_units.add(unit) total += value * unit_to_seconds[unit] pos = match.end() if pos != len(s): # trailing malformed text raise ValueError(f"Malformed duration: {s!r}") return total # --- assert-based tests --- assert parse_duration("1h30m") == 5400 assert parse_duration("1h2m3s") == 3723 raised = False try: parse_duration("1h70x") # malformed: unknown unit 'x' except ValueError: raised = True assert raised ```
google/gemini-3.1-pro-preview: B — Response 2 followed the instructions perfectly, providing exactly three assert-based tests as requested. Response 1 provided more than three tests, which slightly deviates from the exact instruction, although the code is correct. / Response 1 followed all instructions perfectly, including providing exactly three assert-based tests. Response 2 provided more than three tests, slightly deviating from the requested count. | x-ai/grok-4.5: B — Both functions correct and raise on malformed. R2 has exactly 3 assert tests as required; R1 has extras and a test wrapper. R2 clearer/more concise for non-tech use. / R1 exactly meets 3-assert requirement with correct flexible parser. R2 exceeds assert count (violates) and over-restricts unit order vs task examples. | deepseek/deepseek-v4-pro: tie (flipped on swap) — Response 1 includes far more than three assert tests, violating the constraint; it's verbose. Response 2 exactly provides three tests, one malformed, concise and useful, though slight correctness edge (accepts '30m1h'). Overall Response 2 wins. / Response 2 enforces expected unit order from examples, making it more accurate. Both include required tests and error handling. Response 2's docstring is clearer for a non-technical user, and code is well-structured. Therefore 2 wins.
tieFind the bugCoding · 8.5 v 9.7Both responses correctly identify the bug, explain it in a single sentence, and provide the corrected code. Response 2 is slightly more concise…
This is meant to return the average of the positive numbers but returns the wrong value. Identify the bug, explain it in one sentence, and give the corrected function. function avgPositive(xs) { let sum = 0, n = 0; for (const x of xs) { if (x > 0) sum += x; n++; } return sum / n; }
## Bug `n++` is executed for **every** element, not just the positive ones, so `n` ends up counting all elements in `xs` instead of only the positive ones — this makes the divisor too large whenever there are non-positive numbers. ## Corrected function ```javascript function avgPositive(xs) { let sum = 0, n = 0; for (const x of xs) { if (x > 0) { sum += x; n++; } } return sum / n; } ``` *(Note: if `xs` has no positive numbers, this will return `NaN` due to division by zero — you may want to handle that case explicitly depending on desired behavior.)*
The bug is that `n` is incremented for **every** element instead of only positive ones, so the function divides by the total count rather than the count of positive numbers. ```js function avgPositive(xs) { let sum = 0, n = 0; for (const x of xs) { if (x > 0) { sum += x; n++; } } return n === 0 ? 0 : sum / n; // or NaN, depending on desired behavior } ```
google/gemini-3.1-pro-preview: B — Both responses correctly identify the bug, explain it in a single sentence, and provide the corrected code. Response 2 is slightly more concise and handles the division by zero edge case directly in the code. / Both correctly identify the bug and explain it in one sentence. Response 1 is slightly better because it includes the division by zero check directly in the code, making it more robust. | x-ai/grok-4.5: tie (flipped on swap) — Both correctly identify the n++ bug in one sentence and supply fixed code. Minor extras (NaN note vs zero-guard) are helpful and equivalent; no constraints broken. / Both spot the n++ bug correctly. R1 gives exactly one-sentence explanation plus solid fix. R2 uses multi-sentence/sectioned format, violating the one-sentence rule. | deepseek/deepseek-v4-pro: tie (flipped on swap) — Both correctly identify the n-increment bug, give a one-sentence explanation, and fix the function. Equally accurate, follow instructions, and clear. No constraint violations. / Both identify the bug correctly and explain in one sentence. Response 1's corrected function safely returns 0 when no positive numbers exist, avoiding NaN—more robust and non-technical friendly. Response 2's note about NaN is less useful and may confuse.
tieSQL without window functionsCoding · 9.7 v 9.7Both responses provide the exact same correct SQL query that meets all constraints. Response 2 is slightly more concise in its explanation, but…
Given tables users(id, email) and orders(id, user_id, created_at, total), write SQL returning the email and order count of every user with more than 3 orders in the last 30 days, most orders first. Do not use window functions. Target Postgres.
```sql SELECT u.email, COUNT(o.id) AS order_count FROM users u JOIN orders o ON o.user_id = u.id WHERE o.created_at >= NOW() - INTERVAL '30 days' GROUP BY u.id, u.email HAVING COUNT(o.id) > 3 ORDER BY order_count DESC; ``` **Notes:** - `JOIN` (inner join) is used since we only care about users who actually have orders. - `GROUP BY u.id, u.email` groups per user (including `id` avoids issues if emails aren't unique, and is generally good practice in Postgres). - `HAVING COUNT(o.id) > 3` filters for users with more than 3 qualifying orders. - `NOW() - INTERVAL '30 days'` filters orders to the last 30 days. - `ORDER BY order_count DESC` sorts by most orders first.
```sql SELECT u.email, COUNT(o.id) AS order_count FROM users AS u JOIN orders AS o ON o.user_id = u.id WHERE o.created_at >= NOW() - INTERVAL '30 days' GROUP BY u.id, u.email HAVING COUNT(o.id) > 3 ORDER BY order_count DESC; ``` This returns each user’s email and number of orders in the last 30 days, only for users with more than 3 orders, sorted by highest count first.
google/gemini-3.1-pro-preview: tie (flipped on swap) — Both responses provide the exact same correct SQL query that meets all constraints. Response 2 is slightly more concise in its explanation, but both are excellent. / Both responses provide the correct SQL query without using window functions. Response 1 is slightly better as it is more concise, which is preferable for a busy professional. | x-ai/grok-4.5: tie — Both provide identical correct Postgres SQL meeting all requirements without window functions. Equally clear, useful, and instruction-compliant. / Both provide identical correct Postgres SQL meeting all requirements without window functions. Equally clear, concise, and useful; notes in 2 add minor value but do not differentiate overall. | deepseek/deepseek-v4-pro: tie (flipped on swap) — Both queries are correct and meet all constraints. Response 1 adds clear explanatory notes, which increases usefulness for a busy non-technical professional, while Response 2 is concise but lacks that extra clarity. / Both queries correctly implement the task with identical logic, no window functions, and valid Postgres syntax. They are equally clear and concise. Response 2's notes add a bit of context, but both are perfectly usable.
tieInfinite useEffectCoding · 9.7 v 9.7Both responses perfectly explain the infinite loop caused by reference inequality and state updates triggering the effect again. Both provide the…
Explain precisely why this React effect loops forever, then give the fixed version. const [items, setItems] = useState([]); useEffect(() => { fetch('/api/items').then(r => r.json()).then(setItems); }, [items]);
# Why It Loops Forever The `useEffect` has `items` in its dependency array. Here's the chain reaction: 1. Component mounts → `items` is `[]` 2. Effect runs (first render) → fetches data → calls `setItems(newData)` 3. `setItems` updates state → component re-renders 4. Because `items` changed (it's a **new array reference**, even if the API returns identical-looking data), React sees a dependency change 5. Effect runs again → fetches again → calls `setItems` again → new array reference again → ... This repeats indefinitely because: - **Arrays/objects are compared by reference**, not deep equality. Even if the fetched data is identical to before, `newArray !== oldArray` in JavaScript. - The effect's own action (`setItems`) directly causes its own dependency to change, creating a feedback loop with no exit condition. # The Fix Since this effect should only fetch **once on mount**, use an empty dependency array: ```jsx const [items, setItems] = useState([]); useEffect(() => { fetch('/api/items') .then(r => r.json()) .then(setItems); }, []); // ✅ empty array = run once on mount ``` ## If You Actually Need to Re-fetch on Some Change If the intent was to re-fetch when *something else* changes (not `items` itself), depend on that specific value instead: ```jsx const [filter, setFilter] = useState('all'); const [items, setItems] = useState([]); useEffect(() => { fetch(`/api/items?filter=${filter}`) .then(r => r.json()) .then(setItems); }, [filter]); // ✅ re-fetch only when filter changes ``` **Key rule:** Never put a piece of state in the dependency array if the effect's own job is to *set* that same state, unless you're intentionally building a controlled polling/retry loop with proper guards.
`useEffect` runs whenever any value in its dependency array changes. In your code: ```jsx const [items, setItems] = useState([]); useEffect(() => { fetch('/api/items').then(r => r.json()).then(setItems); }, [items]); ``` ### Why it loops forever 1. Initial render: `items` is `[]`. 2. Effect runs (because it always runs after first render). 3. Fetch resolves, `setItems(newArray)` is called. 4. State update causes re-render. 5. `items` dependency is now a **new array reference** (even if same contents). 6. React sees `items` changed → runs effect again. 7. Repeat indefinitely. So the loop is: **effect sets `items` → `items` change retriggers effect**. --- ### Fixed version (fetch once on mount) ```jsx const [items, setItems] = useState([]); useEffect(() => { fetch('/api/items') .then(r => r.json()) .then(setItems); }, []); // no items dependency ``` If you intended refetching based on some external trigger, depend on that trigger (e.g., `query`, `userId`, `refreshKey`), not on the state you set inside the effect.
google/gemini-3.1-pro-preview: tie — Both responses perfectly explain the infinite loop caused by reference inequality and state updates triggering the effect again. Both provide the correct fix (empty dependency array) and offer helpful context for when you actually want to refetch. / Both responses accurately explain the infinite loop caused by updating a state variable that is also in the dependency array, and both provide the correct fix. They are clear, concise, and highly useful. | x-ai/grok-4.5: tie — Both correctly diagnose the reference-equality loop from setItems + [items] and supply the empty-deps fix plus alternatives. Equally clear, accurate, and useful for a non-technical reader. / Both correctly explain the reference-change loop from setItems and give the empty-deps fix. Equally clear, accurate, and useful for a non-technical reader; no constraints broken. | deepseek/deepseek-v4-pro: tie — Both accurately explain the loop (reference equality) and provide the correct fix (empty array). They fully follow the instruction to explain precisely and give a fixed version. Both are clear and useful for a non-technical professional, with no extra constraints. / Both responses accurately explain the loop (new array reference triggers re-run) and provide the correct fix (empty dependency array). They follow instructions, are clear, and useful. No constraints broken.
GitHub CopilotTyped debounceCoding · 6.7 v 9.3Response 1 violates the negative constraint by using the word 'any' multiple times in its explanation. Response 2 follows all instructions…
Write a debounce function in TypeScript that preserves the argument types of the wrapped function, returns a function with a .cancel() method, and does not use 'any'. Explain in one sentence why the naive generic signature loses type information.
## Debounce Implementation ```typescript type Debounced<Args extends unknown[]> = ((...args: Args) => void) & { cancel: () => void; }; function debounce<Args extends unknown[]>( fn: (...args: Args) => void, delayMs: number ): Debounced<Args> { let timeoutId: ReturnType<typeof setTimeout> | undefined; const debounced = (...args: Args): void => { if (timeoutId !== undefined) { clearTimeout(timeoutId); } timeoutId = setTimeout(() => { timeoutId = undefined; fn(...args); }, delayMs); }; debounced.cancel = (): void => { if (timeoutId !== undefined) { clearTimeout(timeoutId); timeoutId = undefined; } }; return debounced; } ``` ### Usage Example ```typescript function greet(name: string, age: number): void { console.log(`${name} is ${age} years old`); } const debouncedGreet = debounce(greet, 300); debouncedGreet("Alice", 30); // ✅ Type-checked correctly debouncedGreet("Bob", "old"); // ❌ Type error: Argument of type 'string' is not assignable to parameter of type 'number' debouncedGreet.cancel(); // ✅ .cancel() is available ``` ### Why the Naive Signature Loses Type Information A naive signature like `function debounce<T extends (...args: any[]) => any>(fn: T, delay: number)` loses type information because the `any[]` constraint causes TypeScript to treat the parameter list as effectively untyped, meaning it will accept a function with *any* parameter types and then allow the returned debounced function to be called with **arbitrary arguments** without triggering type errors — this happens because `any` is contagious and disables type checking anywhere it appears, unlike `unknown[]`, which forces TypeScript to preserve and enforce the actual tuple type of `Args`.
```ts type Debounced<F extends (...args: unknown[]) => unknown> = ((...args: Parameters<F>) => void) & { cancel: () => void }; export function debounce<F extends (...args: unknown[]) => unknown>( fn: F, waitMs: number ): Debounced<F> { let timer: ReturnType<typeof setTimeout> | undefined; const wrapped = ((...args: Parameters<F>) => { if (timer !== undefined) { clearTimeout(timer); } timer = setTimeout(() => { fn(...args); }, waitMs); }) as Debounced<F>; wrapped.cancel = () => { if (timer !== undefined) { clearTimeout(timer); timer = undefined; } }; return wrapped; } ``` Example usage (argument types are preserved): ```ts const save = (id: number, label: string) => { console.log(id, label); }; const debouncedSave = debounce(save, 300); debouncedSave(42, "draft"); // ✅ typed as (number, string) debouncedSave.cancel(); // ✅ cancel method exists ``` A naive generic signature loses type information because typing the wrapper as a broad function shape (like `Function` or `(...args: unknown[]) => unknown`) widens away the original parameter tuple, so the returned function no longer knows the exact argument types of `fn`.
google/gemini-3.1-pro-preview: B — Response 1 violates the negative constraint by using the word 'any' multiple times in its explanation. Response 2 follows all instructions, provides a correct TypeScript implementation without using 'any', and includes a clear one-sentence explanation. / Response 1 follows all instructions, including the negative constraint to not use 'any'. Response 2 violates the negative constraint by using the word 'any' in its explanation. | x-ai/grok-4.5: tie (flipped on swap) — Both correctly implement typed debounce with cancel, avoid any, and give a valid one-sentence explanation. Equally clear, correct, and useful. / Both correctly implement typed debounce with cancel and no any. R1 explanation is exactly one sentence; R2 uses multiple sentences and a heading, violating the constraint. R1 is clearer and more concise. | deepseek/deepseek-v4-pro: B — Both are correct and avoid 'any'. Response 2's explanation is more concise and its use of Parameters<F> is slightly more idiomatic, making it clearer for a busy professional. / Both are correct and avoid 'any'. Response 1's explanation is clearer and more concise for a non-technical reader, while Response 2's is slightly more technical and verbose.
CursorLeftmost binary searchCoding · 9.7 v 8.8Both responses provide correct code, complexity, and a distinguishing test case. Response 1 is slightly better because it provides a more detailed…
Implement binary search that returns the index of the FIRST occurrence of a target in a sorted array with duplicates, or -1. Give the code and state the complexity. Include the test case that distinguishes it from an ordinary binary search.
# First-Occurrence Binary Search ## Code ```python def find_first_occurrence(arr, target): """ Returns the index of the FIRST occurrence of target in a sorted array. Returns -1 if target is not found. """ left, right = 0, len(arr) - 1 result = -1 while left <= right: mid = left + (right - left) // 2 if arr[mid] == target: result = mid # record this match right = mid - 1 # keep searching LEFT for an earlier one elif arr[mid] < target: left = mid + 1 else: right = mid - 1 return result ``` ### Key idea Ordinary binary search stops as soon as it finds *any* index where `arr[mid] == target`. This version, upon finding a match, **does not stop** — it records the index and continues searching the left half (`right = mid - 1`) to see if an earlier occurrence exists. The search space always shrinks, so termination is guaranteed, and `result` always holds the leftmost match found so far. ## Complexity - **Time:** O(log n) — still halves the search space each iteration. - **Space:** O(1) — iterative, constant extra space. ## Distinguishing Test Case ```python arr = [1, 2, 2, 2, 2, 2, 3, 4] target = 2 # Ordinary binary search (stops at first match found): # left=0, right=7, mid=3 -> arr[3]=2 == target -> returns 3 (or similar) # This function must return index 1 (the FIRST occurrence) assert find_first_occurrence(arr, target) == 1 ``` **Why this distinguishes it:** - A standard binary search checks `mid = (0+7)//2 = 3`, sees `arr[3] == 2`, and immediately returns `3` — a *correct* match, but not the *first* one. - This implementation, upon hitting `arr[3] == 2`, saves `result = 3` but then narrows to `right = 2` and keeps searching left. It next checks `mid = 1`, finds `arr[1] == 2`, updates `result = 1`, and narrows further to `right = 0`. Eventually it correctly settles on **index 1**, which is where run of `2`s actually begins. This test case (array with a long run of duplicates where the target's first index is far from the "naive" midpoint) is exactly the scenario where an ordinary binary search would return a valid-but-wrong index, while this implementation returns the correct leftmost index.
Here’s a binary search that returns the **first occurrence** of `target` in a sorted array (with duplicates), or `-1` if not found. ```python def first_occurrence(arr, target): left, right = 0, len(arr) - 1 ans = -1 while left <= right: mid = (left + right) // 2 if arr[mid] == target: ans = mid # potential answer right = mid - 1 # keep searching left half for first occurrence elif arr[mid] < target: left = mid + 1 else: right = mid - 1 return ans ``` ### Complexity - **Time:** `O(log n)` - **Space:** `O(1)` ### Key test case (distinguishes from ordinary binary search) ```python arr = [1, 2, 2, 2, 3] target = 2 print(first_occurrence(arr, target)) # 1 ``` - An ordinary binary search might return index `2` (or `3`), depending on implementation. - This version correctly returns the **first** index: `1`.
google/gemini-3.1-pro-preview: A — Both responses provide correct code, complexity, and a distinguishing test case. Response 1 is slightly better because it provides a more detailed explanation of exactly how the test case behaves differently in both algorithms, which adds clarity. / Both responses perfectly implement the requested algorithm, state the complexities, and provide the distinguishing test case. Response 2 provides slightly better explanations making it more useful and clear. | x-ai/grok-4.5: A — Both correct with O(log n) code and distinguishing tests. R1 more thorough explanation and stronger test case; slightly clearer/more useful. / Both correct with proper first-occurrence logic, O(log n)/O(1), and distinguishing tests. R2 more thorough explanation and clearer distinction makes it slightly more useful. | deepseek/deepseek-v4-pro: tie (flipped on swap) — Both are correct and include code, complexity, and distinguishing test case. Response 1 offers a more detailed explanation and a longer duplicate run, enhancing clarity for a non-technical reader. / Both correctly implement first-occurrence binary search with O(log n) complexity and provide distinguishing test cases. Response 2 adds extra explanation but both are equally correct, clear, and useful.
tieSecurity reviewCoding · 9.3 v 9.2Both responses correctly identify the vulnerabilities, order them by severity, and provide clear fixes. Response 2's final code example is…
Review this Express handler and list every security problem you find, most severe first, each with the fix. app.get('/file', (req, res) => { const p = req.query.name; db.query(`SELECT * FROM files WHERE name = '${p}'`, (e, rows) => { res.sendFile(__dirname + '/uploads/' + p); }); });
# Security Issues (Most Severe First) ## 1. SQL Injection (Critical) ```js db.query(`SELECT * FROM files WHERE name = '${p}'`, ...) ``` User input `p` is directly concatenated into the SQL string. An attacker can inject arbitrary SQL via `?name=' OR '1'='1' --` or worse, chain destructive queries (if the driver/permissions allow it). **Fix:** Use parameterized queries. ```js db.query('SELECT * FROM files WHERE name = ?', [p], (e, rows) => { ... }); ``` ## 2. Path Traversal / Arbitrary File Read (Critical) ```js res.sendFile(__dirname + '/uploads/' + p); ``` `p` is user-controlled and concatenated directly into a filesystem path. An attacker can request `?name=../../etc/passwd` or similar to read files outside the `uploads` directory (including source code, `.env` secrets, SSH keys, etc.). **Fix:** Resolve and validate the final path stays within the intended directory, or better, let `sendFile` handle root confinement via its `root` option (which blocks `..` traversal) plus explicit validation: ```js const path = require('path'); app.get('/file', (req, res) => { const name = req.query.name; // Whitelist safe characters — no slashes, no dots that could traverse if (typeof name !== 'string' || !/^[a-zA-Z0-9._-]+$/.test(name)) { return res.status(400).send('Invalid file name'); } db.query('SELECT * FROM files WHERE name = ?', [name], (e, rows) => { if (e) return res.status(500).send('Server error'); if (!rows.length) return res.status(404).send('Not found'); res.sendFile(name, { root: path.join(__dirname, 'uploads') }, (err) => { if (err) res.status(err.status || 500).end(); }); }); }); ``` Using the `root` option makes Express reject any resolved path that escapes the root directory, even if validation is imperfect — defense in depth. ## 3. No Authorization Check (High) The handler never verifies that the requesting user is allowed to access the requested file. Even if the DB row exists, there's no ownership/ACL check — this is an **IDOR** (Insecure Direct Object Reference) vulnerability. Any authenticated (or even unauthenticated) user can fetch any file by guessing/enumerating names. **Fix:** Check the file record belongs to the authenticated user (or the user has permission) before serving it: ```js db.query('SELECT * FROM files WHERE name = ? AND owner_id = ?', [name, req.user.id], ...) ``` ## 4. Missing Error Handling for DB Query (Medium) The callback ignores the error `e` and the query result `rows` entirely — it serves the file regardless of whether the DB lookup succeeded, found a match, or errored. This means: - The DB query is essentially pointless/decorative (file is served even if no matching row exists). - DB errors are silently swallowed, potentially leaking stack traces later or causing undefined behavior. **Fix:** Check `e` and `rows.length` before proceeding (shown in the fixed code above). ## 5. No Input Type/Presence Validation (Medium) `req.query.name` could be `undefined`, an array (`?name=a&name=b`), or an object (`?name[foo]=bar`), since Express's query parser allows nested structures. Concatenating a non-string into SQL or a path can cause unexpected behavior or bypass naive filters. **Fix:** Explicitly validate that `name` is a non-empty string matching an expected pattern (as in the whitelist regex above) before use. ## 6. Information Disclosure via Errors (Low) If `sendFile` fails (e.g., file not found), Express's default behavior may leak stack traces or file paths in the response depending on environment settings. **Fix:** Always pass an explicit error-handling callback to `sendFile` and return a generic error message, and ensure `NODE_ENV=production` disables verbose error pages. --- ### Summary Table | # | Issue | Severity | |---|-------|----------| | 1 | SQL Injection | Critical | | 2 | Path Traversal | Critical | | 3 | Missing Authorization (IDOR) | High | | 4 | Ignored DB errors/results | Medium | | 5 | No input validation | Medium | | 6 | Verbose error responses | Low |
Here are the security issues, **highest severity first**, with fixes. 1. **SQL Injection (critical)** ```js db.query(`SELECT * FROM files WHERE name = '${p}'`, ...) ``` User input goes directly into SQL. An attacker can inject SQL (`' OR 1=1 --`, etc.). **Fix:** Use parameterized queries / prepared statements. ```js db.query('SELECT * FROM files WHERE name = ?', [p], ...) ``` 2. **Path Traversal / Arbitrary File Read (critical)** ```js res.sendFile(__dirname + '/uploads/' + p); ``` If `p` is `../../../../etc/passwd`, the app may serve files outside uploads. **Fix:** Never concatenate untrusted paths. Validate filename strictly and resolve against a fixed root. - Allow only expected filename pattern (e.g., UUID + extension). - Use `path.resolve` and verify it stays inside upload dir. - Or use `sendFile(name, { root: UPLOAD_DIR })` with strict validation. 3. **No authorization check (critical/high)** Anyone who can hit `/file?name=...` can request files. Even if SQL/path are fixed, this is still insecure if files are private. **Fix:** Require authentication and verify caller is allowed to access that specific file (ownership/ACL check in DB). 4. **TOCTOU / logic bug: DB result is ignored (high)** Query runs, but result is never checked. Even if file name doesn’t exist in DB (or user shouldn’t access it), code still sends file based only on `p`. **Fix:** Enforce authorization/existence using query result before sending file: - If DB says no row or not owned by user → 404/403. - Only then send the corresponding stored filename. 5. **No error handling for DB or file send (medium)** `e` is ignored; `sendFile` errors aren’t handled. This can leak behavior, crash in some setups, or return confusing responses. **Fix:** Handle DB and file errors explicitly; return generic messages. ```js if (e) return res.status(500).send('Internal error'); res.sendFile(..., err => { if (err) ... }); ``` 6. **Unvalidated input (medium)** `req.query.name` may be missing, huge, malformed, or contain special chars. **Fix:** Validate type/length/format early (e.g., max length, regex allowlist, required extension). --- ### Safer pattern (example) ```js const path = require('path'); const UPLOAD_DIR = path.join(__dirname, 'uploads'); app.get('/file', requireAuth, (req, res) => { const name = req.query.name; // 1) Validate input (example: simple filename only) if (typeof name !== 'string' || !/^[a-zA-Z0-9._-]{1,100}$/.test(name)) { return res.status(400).send('Invalid file name'); } // 2) AuthZ + existence check via parameterized query db.query( 'SELECT stored_name FROM files WHERE name = ? AND owner_id = ?', [name, req.user.id], (e, rows) => { if (e) return res.status(500).send('Internal error'); if (!rows || rows.length === 0) return res.status(404).send('Not found'); const stored = rows[0].stored_name; // 3) Safe file send from fixed root if (!/^[a-zA-Z0-9._-]{1,100}$/.test(stored)) { return res.status(500).send('Internal error'); } res.sendFile(stored, { root: UPLOAD_DIR }, (err) => { if (err) { if (!res.headersSent) res.status(err.statusCode || 404).send('Not found'); } }); } ); }); ``` If you want, I can also provide a hardened version using async/await + centralized error middleware.
google/gemini-3.1-pro-preview: tie (flipped on swap) — Both responses correctly identify the vulnerabilities, order them by severity, and provide clear fixes. Response 2's final code example is slightly more robust by using the database result for the file path rather than the user input. / Both responses correctly identify all the security vulnerabilities in the correct severity order and provide excellent fixes. Response 2 is slightly better formatted and provides a clearer explanation of how Express handles query parameters and the sendFile root option. | x-ai/grok-4.5: tie — Both correctly identify SQLi, path traversal, missing auth, ignored DB results, validation and error issues with solid fixes ordered by severity. Equally clear, complete and useful for the task. / Both correctly identify SQLi, path traversal, missing auth, ignored DB results, and validation issues with solid fixes. Equally clear, complete, and useful; no constraints broken. | deepseek/deepseek-v4-pro: tie (flipped on swap) — Response 1 is more organized with a summary table and covers information disclosure, making it clearer and slightly more complete. Both are accurate and follow instructions. / Both responses correctly identify all critical issues (SQL injection, path traversal, missing auth, ignored DB result, missing error handling, input validation) in severity order with fixes. They are equally accurate, clear, and useful.
tieSafe migrationCoding · 8 v 8.3Response 2 provides the classic, safe multi-step approach clearly and concisely. Response 1 contains a factual error regarding Postgres 11+…
Write the Postgres migration to add a NOT NULL column 'status' with default 'pending' to an orders table with 40 million rows, without taking a long exclusive lock. Give the steps in order and say which step is the dangerous one and why.
# Postgres Migration: Adding NOT NULL Column with Default to Large Table ## The Problem In older Postgres (<11), adding a column with a default rewrites the entire table under an `ACCESS EXCLUSIVE` lock — on 40M rows, this blocks all reads/writes for potentially minutes. In **Postgres 11+**, adding a column with a *constant* default is fast (metadata-only) because Postgres stores the default separately and applies it lazily on read. But `NOT NULL` still requires a full table scan to validate, and if done as a single `ADD COLUMN ... NOT NULL DEFAULT ...` combined with other steps carelessly, or if you also need a backfill for non-constant logic, you can still trigger long locks or long validation scans. The safe pattern separates: **add column (nullable, with default)** → **backfill if needed** → **add constraint concurrently via NOT VALID + VALIDATE**. ## Migration Steps (in order) ### Step 1 — Add the column as nullable, with a default ```sql ALTER TABLE orders ADD COLUMN status text DEFAULT 'pending'; ``` - Fast in PG11+: no rewrite, no full table lock (just a brief metadata lock). - Deliberately **not** `NOT NULL` yet — that's the risky part we're isolating. ### Step 2 — Backfill existing rows (if any rows predate the default logic, or to be safe/explicit) ```sql -- Only needed if some rows could be NULL, e.g. inserted between deploy steps -- Do this in batches to avoid long transactions / bloat / replication lag UPDATE orders SET status = 'pending' WHERE status IS NULL AND id BETWEEN 1 AND 100000; -- repeat in batches (or use a script/loop with a small sleep between batches) ``` - Since the column already has a default, existing rows read via the default mechanism will already show `'pending'` — so this step is often **unnecessary** in PG11+. Include it only if you're worried about concurrent inserts happening without the default applying correctly, or you're on PG <11. ### Step 3 — Add the NOT NULL constraint as NOT VALID (doesn't scan table) ```sql ALTER TABLE orders ADD CONSTRAINT orders_status_not_null CHECK (status IS NOT NULL) NOT VALID; ``` - This takes only a quick lock to add the constraint definition — no table scan yet. ### Step 4 — Validate the constraint (this is where the scan happens) ```sql ALTER TABLE orders VALIDATE CONSTRAINT orders_status_not_null; ``` - Scans the table to confirm no NULLs, but uses a `SHARE UPDATE EXCLUSIVE` lock, which **allows concurrent reads and writes** (just blocks other schema changes). This is the safe way to validate on a live table. ### Step 5 (optional cleanup) — Convert CHECK constraint into a real NOT NULL Once validated, you can promote it to an actual column-level `NOT NULL`, which Postgres will apply instantly because it trusts the already-validated CHECK constraint: ```sql ALTER TABLE orders ALTER COLUMN status SET NOT NULL; ALTER TABLE orders DROP CONSTRAINT orders_status_not_null; ``` - The `SET NOT NULL` here is fast because Postgres (12+) detects the equivalent valid CHECK constraint and skips the re-scan. ## Which Step Is Dangerous, and Why **The dangerous step is doing `ADD COLUMN status text NOT NULL DEFAULT 'pending'` directly in one statement** (i.e., skipping the NOT VALID/VALIDATE split) — or equivalently, running `ALTER TABLE ... ALTER COLUMN status SET NOT NULL` directly without first validating via a `NOT VALID` CHECK constraint. Why it's dangerous: - `SET NOT NULL` (or a combined `ADD COLUMN ... NOT NULL`) requires Postgres to verify **every row** has a non-null value. - Without a pre-validated CHECK constraint to shortcut the check, this triggers a **full table scan while holding `ACCESS EXCLUSIVE` lock**. - On a 40M-row table, that scan can take from seconds to many minutes depending on I/O — and during that entire time, **all reads and writes to `orders` are blocked**, likely causing application timeouts/outages. The `NOT VALID` → `VALIDATE CONSTRAINT` split is the key technique: it moves the expensive full-table scan into a step that only takes a `SHARE UPDATE EXCLUSIVE` lock, which doesn't block normal traffic.
For a **40M-row** table, do this as a **multi-step migration** so you avoid a long blocking lock. ## Safe migration plan (in order) ### 1) Add the column as nullable first ```sql ALTER TABLE orders ADD COLUMN status text; ``` - Fast metadata change. - Takes an `ACCESS EXCLUSIVE` lock briefly, but no full-table rewrite/scan. --- ### 2) Set default for new rows ```sql ALTER TABLE orders ALTER COLUMN status SET DEFAULT 'pending'; ``` - New inserts get `'pending'`. - Also a brief lock. --- ### 3) Backfill existing rows in batches Do this in small chunks (outside one huge transaction), e.g. repeatedly: ```sql UPDATE orders SET status = 'pending' WHERE ctid IN ( SELECT ctid FROM orders WHERE status IS NULL LIMIT 10000 ); ``` Repeat until: ```sql SELECT count(*) FROM orders WHERE status IS NULL; -- should be 0 ``` --- ### 4) Add a NOT VALID check constraint ```sql ALTER TABLE orders ADD CONSTRAINT orders_status_not_null_chk CHECK (status IS NOT NULL) NOT VALID; ``` - Quick to add; doesn’t scan all rows yet. --- ### 5) Validate the check constraint ```sql ALTER TABLE orders VALIDATE CONSTRAINT orders_status_not_null_chk; ``` - Scans table but uses lighter locking than a hard `SET NOT NULL` scan. --- ### 6) Convert to real NOT NULL ```sql ALTER TABLE orders ALTER COLUMN status SET NOT NULL; ``` - With validated check already in place, this is typically quick. (Optional) drop the redundant check constraint after: ```sql ALTER TABLE orders DROP CONSTRAINT orders_status_not_null_chk; ``` --- ## The dangerous step (and why) **Dangerous step: `ALTER TABLE ... ALTER COLUMN status SET NOT NULL`** (step 6) If you do this **without** pre-backfill + validated check, Postgres may need to scan the whole table while holding a stronger lock, which can block traffic for a long time on 40M rows. Also, even in the optimized flow, step 6 still needs to acquire `ACCESS EXCLUSIVE`; if long transactions are open, lock acquisition can wait and cause blocking. Use low `lock_timeout` and run during low traffic. --- If you want, I can also give you a production-ready migration script with retry/lock-timeout settings and a batch backfill loop.
google/gemini-3.1-pro-preview: B — Response 2 provides the classic, safe multi-step approach clearly and concisely. Response 1 contains a factual error regarding Postgres 11+: adding a new column with a constant DEFAULT and NOT NULL in a single statement is actually a fast, metadata-only operation that does not require a table scan. / Response 1 provides a standard, safe multi-step migration plan and correctly identifies the dangerous step. Response 2 contains a factual error regarding Postgres 11+: adding a column with a constant DEFAULT and NOT NULL in a single statement is actually a fast, metadata-only operation in PG11+. | x-ai/grok-4.5: A — Both give correct multi-step safe migration using NOT VALID/VALIDATE. R1 better on PG11+ default behavior and lock details; R2 solid on batching but slightly less precise on defaults/danger. / Both give correct multi-step NOT VALID pattern. R2 more precise on PG11+ defaults and lock levels; clearer danger explanation. R1 solid but slightly less accurate on when backfill needed. | deepseek/deepseek-v4-pro: tie (flipped on swap) — Response 2 is more accurate, avoids claiming the combined ADD COLUMN … NOT NULL DEFAULT is always dangerous (it is fast on PG11+). Its numbered steps and concise explanation better serve a busy non-technical professional. Response 1 mistakenly says NOT NULL still requires a full scan on PG11+ in that combined case, which is misleading. / Both are correct. Response 2 uses a more standard batching approach (id range vs. CTID), making it clearer and easier for a busy non‑technical user. Response 1’s CTID‑based backfill is less practical and may confuse.
tieFix the code not the testCoding · 9.5 v 9.3Both responses correctly fix the implementation to pass the test. Response 1 provides a clear explanation, while Response 2 adds an extra regex to…
This test fails. Fix the implementation, not the test. // impl export const slugify = (s) => s.toLowerCase().replace(/ /g, '-'); // test expect(slugify(' Hello World! ')).toBe('hello-world');
```javascript export const slugify = (s) => s .toLowerCase() .trim() .replace(/[^a-z0-9\s-]/g, '') .replace(/\s+/g, '-'); ``` **Issue:** The original implementation only lowercased the string and replaced single spaces with hyphens. It didn't: 1. Trim leading/trailing whitespace 2. Collapse multiple consecutive spaces into one hyphen 3. Remove punctuation/special characters (like `!`) **Fix explanation:** - `.trim()` removes leading/trailing whitespace → `"Hello World!"` - `.replace(/[^a-z0-9\s-]/g, '')` strips out special characters like `!` → `"Hello World"` - `.replace(/\s+/g, '-')` collapses one or more whitespace characters into a single hyphen → `"hello-world"` This produces `'hello-world'` as expected. ✅
You're right — the implementation is too naive. It only lowercases and replaces single spaces, so it leaves leading/trailing dashes, multiple dashes, and punctuation. Use this instead: ```js export const slugify = (s) => s .toLowerCase() .trim() .replace(/[^\w\s-]/g, '') // remove punctuation (like !) .replace(/\s+/g, '-') // collapse whitespace to single dash .replace(/-+/g, '-'); // collapse repeated dashes ``` Now this passes: ```js expect(slugify(' Hello World! ')).toBe('hello-world'); ```
google/gemini-3.1-pro-preview: tie (flipped on swap) — Both responses correctly fix the implementation to pass the test. Response 1 provides a clear explanation, while Response 2 adds an extra regex to handle repeated dashes, making it slightly more robust for general use. Both are excellent. / Both responses correctly fix the implementation to pass the test. Response 2 is slightly better because its regex explicitly targets alphanumeric characters, which is safer for slugs than Response 1's use of \w (which includes underscores). Response 2 also provides a clearer breakdown of the fix. | x-ai/grok-4.5: tie — Both correctly fix slugify to pass the test via trim, strip punctuation, and collapse spaces. Clear explanations; equally useful and concise. No constraints broken. / Both correctly fix slugify to pass the test via trim, punctuation removal, and space collapsing. Equally follow the fix-impl instruction, useful, and clear. | deepseek/deepseek-v4-pro: abstain — unparseable
tieRace conditionCoding · 9.5 v 8.7Both responses correctly identify the race condition, explain the interleaving, and provide a working fix. Response 1's solution is more elegant…
Identify the race condition in this code, explain what interleaving causes it, and fix it. let cache = null; async function getConfig() { if (cache) return cache; const r = await fetch('/config'); cache = await r.json(); return cache; }
## The Race Condition **The problem:** `getConfig()` has an `await` between the check (`if (cache)`) and the write (`cache = ...`). Since JavaScript async functions yield control at `await` points, multiple concurrent calls can all pass the `if (cache)` check before any of them finishes setting `cache`. ### Interleaving that triggers it ``` Call A: getConfig() invoked → cache is null → skips return → starts fetch('/config') → awaits (suspends) Call B: getConfig() invoked → cache is still null (A hasn't set it yet) → skips return → starts a SECOND fetch('/config') → awaits (suspends) Call A: fetch resolves → cache = json → returns cache Call B: fetch resolves → cache = json (overwrites) → returns cache ``` **Consequences:** - Duplicate network requests fire for every caller that arrives before the first fetch resolves (wasteful, and can hammer the backend on page load if many components call `getConfig()`). - Each caller gets whatever value was resolved *at the time*, not necessarily the final cached one — if the responses differ or arrive out of order, callers can get inconsistent objects. - No error handling: if the fetch throws, `cache` stays `null` forever, but concurrent callers that already started their own fetch may still succeed or fail independently, making the failure behavior nondeterministic depending on timing. ## The Fix Cache the **in-flight promise**, not just the resolved value. Every caller — whether the fetch is in progress or already done — awaits the same promise. ```js let cachePromise = null; function getConfig() { if (!cachePromise) { cachePromise = fetch('/config') .then(r => r.json()) .catch(err => { cachePromise = null; // allow retry on failure throw err; }); } return cachePromise; } ``` ### Why this works - The check-and-set (`if (!cachePromise) cachePromise = ...`) happens **synchronously**, with no `await` in between — so there's no window where two calls can both see "empty" and both start a fetch. - All concurrent callers receive the *same promise* and thus the same resolved value once it settles. - The `.catch` resets `cachePromise` to `null` so a failed fetch doesn't permanently poison the cache — the next call will retry instead of returning a rejected promise forever. **Note:** `getConfig` no longer needs to be declared `async` since it just returns a promise directly (returning a promise from a non-async function is equivalent to awaiting/re-wrapping it, and avoids an unnecessary microtask hop).
You have a classic **check-then-act race** on `cache`. ### What’s the race? `if (cache) return cache;` and `cache = ...` are separated by `await`, so multiple callers can pass the check before any one of them stores the result. ### Problematic interleaving Assume `cache === null`: 1. Call A enters `getConfig()`, sees `cache` null. 2. A starts `fetch('/config')`, then yields at `await`. 3. Call B enters before A finishes, also sees `cache` null. 4. B also starts `fetch('/config')`. 5. Now you have duplicate requests; whichever finishes last writes `cache` last. Even in JS’s single-threaded model, async suspension allows this interleaving. --- ### Fix: cache the **in-flight promise** (single-flight) Store the pending request immediately, so concurrent callers share it. ```js let cache = null; let inFlight = null; async function getConfig() { if (cache) return cache; if (inFlight) return inFlight; inFlight = (async () => { const r = await fetch('/config'); const data = await r.json(); cache = data; return data; })(); try { return await inFlight; } finally { // Clear in-flight once settled (success or failure) inFlight = null; } } ``` ### Why this works - First caller creates `inFlight` immediately. - Subsequent callers see `inFlight` and await the same promise. - Only one network request is made. - On failure, `inFlight` is cleared so retries can happen.
google/gemini-3.1-pro-preview: A — Both responses correctly identify the race condition, explain the interleaving, and provide a working fix. Response 1's solution is more elegant and idiomatic by simply caching the promise itself, whereas Response 2 uses a slightly more complex two-variable approach. / Both responses correctly identify the race condition and explain the interleaving. Response 2 provides a much cleaner, more idiomatic JavaScript solution by caching the promise directly, whereas Response 1's solution is overly complex with an async IIFE and try/finally block. | x-ai/grok-4.5: tie (flipped on swap) — Both correctly identify the check-then-act race via await interleaving and fix via promise caching. R1 is cleaner (single var, sync check, proper retry), more precise on consequences, and clearer for busy readers. / Both correctly identify the check-then-act race across await, show valid interleavings, and give equivalent single-flight promise fixes. Equally clear, useful, and accurate for a non-technical reader. | deepseek/deepseek-v4-pro: tie (flipped on swap) — Both correctly identify the race, explain interleaving, and provide valid fixes. Response 1 is slightly more concise, notes that async is unnecessary, and its fix is simpler with a single promise variable. Response 2's fix is also correct but uses an extra variable and try/finally, making it marginally less direct. / Both responses correctly identify the race condition, explain interleaving, and provide a working fix. They are equally clear and useful.
tieRetry with backoffCoding · 9.3 v 8Response 1 followed all instructions and constraints, including the maximum 30 lines limit (it is 28 lines). Response 2 exceeded the 30-line limit…
Write an async retry wrapper in TypeScript: exponential backoff with jitter, a maximum attempt count, and it must NOT retry on 4xx responses other than 429. Maximum 30 lines. State what happens on the final failure.
```typescript type RetryOpts = { attempts?: number; baseMs?: number; maxMs?: number }; async function withRetry<T>( fn: () => Promise<T>, { attempts = 5, baseMs = 200, maxMs = 5000 }: RetryOpts = {} ): Promise<T> { let lastErr: unknown; for (let attempt = 1; attempt <= attempts; attempt++) { try { return await fn(); } catch (err: any) { lastErr = err; const status = err?.status ?? err?.response?.status; const isNonRetryableClientErr = status >= 400 && status < 500 && status !== 429; if (isNonRetryableClientErr || attempt === attempts) throw err; const exp = Math.min(maxMs, baseMs * 2 ** (attempt - 1)); const delay = Math.random() * exp; // full jitter await new Promise((r) => setTimeout(r, delay)); } } throw lastErr; // unreachable, satisfies TS } ``` **On final failure:** after `attempts` tries are exhausted, the wrapper rethrows the *last* error encountered — it does not swallow it. The returned promise rejects with that original error, so the caller must handle it via `try/catch` or `.catch()`. Non-retryable 4xx errors (except 429) short-circuit immediately and are thrown on the first occurrence, without waiting or consuming retry attempts.
```ts type RetryOpts = { maxAttempts?: number; baseDelayMs?: number; maxDelayMs?: number }; const sleep = (ms: number) => new Promise(r => setTimeout(r, ms)); export async function retryWithBackoff<T>( fn: () => Promise<T>, { maxAttempts = 5, baseDelayMs = 200, maxDelayMs = 5000 }: RetryOpts = {} ): Promise<T> { let lastErr: unknown; for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await fn(); } catch (err: any) { lastErr = err; const status = err?.response?.status ?? err?.status; // fetch/axios-style const is4xx = typeof status === "number" && status >= 400 && status < 500; const retryable = !is4xx || status === 429; // never retry other 4xx if (!retryable || attempt === maxAttempts) throw err; const exp = Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1)); const jitter = Math.random() * exp; // full jitter [0, exp) await sleep(jitter); } } throw lastErr; // unreachable, keeps TS happy } ``` On final failure, it throws/rejects with the **last caught error** (either immediately for non-retryable 4xx or after exhausting attempts).
google/gemini-3.1-pro-preview: A — Response 1 followed all instructions and constraints, including the maximum 30 lines limit (it is 28 lines). Response 2 exceeded the 30-line limit (it is 33 lines). Both provided accurate, clear, and useful code. / Response 1 violates the maximum 30 lines constraint (it has 33 lines). Response 2 follows all instructions, including the line constraint, and provides a correct, concise implementation. | x-ai/grok-4.5: tie — Both correctly implement async retry with exp backoff+jitter, max attempts, skip non-429 4xx; under 30 lines; clearly state final rethrow. Equivalent quality. / Both correctly implement async retry with exp backoff+jitter, max attempts, skip non-429 4xx; under 30 lines; state final throw of last err. Equally clear/useful. | deepseek/deepseek-v4-pro: tie (flipped on swap) — Both correctly implement exponential backoff with jitter, max attempts, exclude non-429 4xx retries, and explain final failure. Both are under 30 lines and clear. No violations. / Response 1 checks typeof status number, uses clear sleep helper, slightly more robust for edge cases.
tieRecursive typeCoding · 9.2 v 9.7Both responses provide accurate TypeScript types and a one-sentence explanation. Response 2 is slightly better as it is more concise and avoids…
Write a TypeScript type DeepPartial<T> that makes every nested property optional, and explain in one sentence how it must handle arrays differently from plain objects.
## `DeepPartial<T>` ```typescript type DeepPartial<T> = T extends (infer U)[] ? DeepPartial<U>[] : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : T extends Function ? T : T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T; ``` ### Example ```typescript interface User { name: string; address: { city: string; zip: string }; tags: string[]; friends: { name: string; age: number }[]; } type PartialUser = DeepPartial<User>; /* { name?: string; address?: { city?: string; zip?: string }; tags?: string[]; friends?: { name?: string; age?: number }[]; } */ ``` ### Explanation (arrays vs. objects) Arrays must be handled by recursively applying `DeepPartial` to their **element type** and keeping the result as an array (`DeepPartial<U>[]`), rather than being turned into a mapped object type with optional numeric-index keys (like plain objects are), because doing the latter would strip away the array's built-in methods, iteration behavior, and length semantics.
```ts type DeepPartial<T> = T extends (...args: any[]) => any ? T : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : T extends Array<infer U> ? Array<DeepPartial<U>> : T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T; ``` Arrays must be treated specially because they should stay arrays whose **elements** are deeply partial, rather than being mapped like plain objects (which would incorrectly make array methods/index signatures optional keys).
google/gemini-3.1-pro-preview: B — Both responses provide accurate TypeScript types and a one-sentence explanation. Response 2 is slightly better as it is more concise and avoids unnecessary examples, making it quicker for a busy professional to digest. / Both responses provide accurate TypeScript types and a one-sentence explanation. Response 1 is preferred for being more concise and directly addressing the prompt without adding unnecessary examples. | x-ai/grok-4.5: tie (flipped on swap) — Both provide correct recursive DeepPartial handling arrays via element type (not mapped keys), with valid one-sentence explanations. Equally clear, useful, and instruction-compliant. / Both types correctly implement DeepPartial with proper array handling. R1 is more concise and exactly one-sentence explanation; R2 adds useful example but is longer/less focused. | deepseek/deepseek-v4-pro: tie — Both types correctly implement DeepPartial, handling arrays, readonly arrays, functions, and objects. Both explanations are one sentence and accurately describe why arrays must be treated differently. No constraints broken. / Both types correctly make nested properties optional and handle arrays by keeping them as arrays with partial elements. Both explanations are one sentence, accurate, and clear. Response 2 adds an example, but that doesn't affect correctness or instruction compliance. Both are equally useful and concise.
tieBash one-linerCoding · 8.7 v 8.8Both responses provide accurate commands that meet all requirements. Response 2 is slightly better as it uses the built-in '-exec ... +' which is…
Give me a single shell command that finds the ten largest files under the current directory recursively and prints them human-readable, largest first. It must handle filenames containing spaces. Explain each part briefly.
# Command ```bash find . -type f -print0 | xargs -0 du -h | sort -rh | head -n 10 ``` ## Breakdown | Part | Purpose | |---|---| | `find . -type f -print0` | Recursively lists all regular files under `.`, separating output with **NUL bytes** (`\0`) instead of newlines/spaces. This is what makes it safe for filenames containing spaces, newlines, or other odd characters. | | `xargs -0 du -h` | Reads the NUL-separated list and runs `du -h` on the files (in batches, for efficiency). `-h` makes `du` print sizes in human-readable form (e.g. `4.0K`, `1.2M`, `3.5G`). `xargs -0` matches `find`'s `-print0` so filenames with spaces aren't split incorrectly. | | `sort -rh` | Sorts the output. `-h` tells `sort` to understand human-readable suffixes (K, M, G, etc.) numerically rather than alphabetically. `-r` reverses the order so the largest come first. | | `head -n 10` | Keeps only the top 10 lines, i.e., the 10 largest files. | ### Why not just `du -ah . | sort -rh | head`? That's simpler and often works, but: - `du -a` includes **directories** too, not just files, unless you filter them out. - `find -type f` explicitly restricts to regular files only. If you don't care about excluding directories, the simpler version works too: ```bash du -ah . | sort -rh | head -n 10 ``` This is safe with spaces because `du`'s output (one path per line) is passed directly to `sort`/`head` without any word-splitting step — no `xargs` or filename parsing happens in between.
```sh find . -type f -exec du -h {} + | sort -hr | head -n 10 ``` Brief breakdown: - `find . -type f` — recursively find regular files under the current directory (`.`). - `-exec du -h {} +` — run `du -h` on those files in batches (`{}` is replaced by filenames). This safely handles spaces in filenames. - `sort -hr` — sort by **human-readable size**, largest first (`-r` = reverse). - `head -n 10` — keep only the top 10 results. If you want exact file sizes (not disk usage), say so and I’ll give a byte-accurate variant.
google/gemini-3.1-pro-preview: B — Both responses provide accurate commands that meet all requirements. Response 2 is slightly better as it uses the built-in '-exec ... +' which is cleaner than piping to xargs, and its explanation is more concise and direct. / Both responses provide correct commands that handle spaces and explain the parts. Response 1 is more concise and adheres better to the 'briefly' constraint, whereas Response 2 includes unnecessary extra information and alternatives. | x-ai/grok-4.5: tie — Both provide correct, space-safe single commands with accurate brief explanations of each part. Equally useful and clear for the audience; minor style differences only. / Both give correct, space-safe single commands with clear brief explanations of each part. Equally accurate, compliant, useful and concise for the request. | deepseek/deepseek-v4-pro: tie (flipped on swap) — Both commands inadvertently include batch totals from du, producing incorrect non-file entries. Response 1 is slightly stronger: it warns against the simpler command's directory issue, gives a clearer breakdown, and mentions an alternative that avoids the bug, despite still presenting a flawed primary command. Response 2 is terse and doesn't address the total-line flaw. / Both commands correctly find the ten largest files with human-readable sizes, safely handle spaces, and give concise explanations. Response 2's table is slightly more explicit, but both are excellent and fully meet the task's requirements.
tieExplain and costCoding · 9.5 v 9.2Both responses perfectly address the prompt, providing accurate explanations, complexities, and the correct O(n) rewrite. Response 1 includes a…
Explain what this does and give its time and space complexity, then rewrite it to be O(n). def has_dup(xs): for i in range(len(xs)): for j in range(i+1, len(xs)): if xs[i] == xs[j]: return True return False
# What it does `has_dup` checks whether a list `xs` contains any duplicate values. It does this by comparing every element to every other element that comes after it (a brute-force pairwise comparison). If any two elements are equal, it returns `True`; if it gets through all pairs without finding a match, it returns `False`. # Complexity - **Time:** O(n²) — for each of the `n` elements, it scans the remaining elements (nested loop), giving roughly n(n-1)/2 comparisons in the worst case. - **Space:** O(1) — no extra data structures are used, just loop indices. # O(n) rewrite Use a hash set to track elements seen so far. Checking membership and inserting into a set are both O(1) average time, so a single pass through the list suffices. ```python def has_dup(xs): seen = set() for x in xs: if x in seen: return True seen.add(x) return False ``` **Complexity of the new version:** - **Time:** O(n) average case (each set lookup/insert is O(1) amortized). - **Space:** O(n) in the worst case (if there are no duplicates, the set grows to hold all `n` elements). This trades space for time — a common and usually worthwhile tradeoff, since O(n) time is far better than O(n²) for large inputs, and O(n) extra space is typically acceptable.
This function checks whether a list has any duplicate values. ### What it does ```python def has_dup(xs): for i in range(len(xs)): for j in range(i+1, len(xs)): if xs[i] == xs[j]: return True return False ``` - It compares each element `xs[i]` with every element after it (`xs[j]`). - If any pair is equal, it returns `True` immediately. - If it finishes all comparisons without finding a match, it returns `False`. ### Time and space complexity - **Time:** **O(n²)** in the worst case (no duplicates), because of the nested loops. - **Space:** **O(1)** extra space (it uses only a few variables). --- ### O(n) rewrite Use a `set` to track values seen so far: ```python def has_dup(xs): seen = set() for x in xs: if x in seen: return True seen.add(x) return False ``` - **Time:** **O(n)** average case (set lookup/insert are average O(1)). - **Space:** **O(n)** extra space for the set.
google/gemini-3.1-pro-preview: A — Both responses perfectly address the prompt, providing accurate explanations, complexities, and the correct O(n) rewrite. Response 1 includes a brief, helpful note about the space-time tradeoff which adds value for the reader. / Both responses correctly explain the code, provide accurate complexities, and offer the standard O(n) set-based rewrite. Response 2 is slightly better for a non-technical professional as it briefly explains the space-time tradeoff in plain language. | x-ai/grok-4.5: tie — Both correctly explain the O(n^{2})/O(1) brute-force check and provide identical correct O(n) set-based rewrites with accurate complexities. Equally clear, useful, and complete; no constraints violated. / Both correctly explain the O(n^{2})/O(1) function and provide equivalent correct O(n) set-based rewrites. Equally clear, complete, and useful; no constraints violated. | deepseek/deepseek-v4-pro: tie — Both responses accurately explain the function, complexities, and provide an O(n) rewrite. They are clear, concise, and equally useful. No constraint violations. / Both accurately describe the function and O(n²) complexity, and provide an O(n) set-based rewrite. Equally clear and concise.
GitHub CopilotRegex with limitsCoding · 5.1 v 8.6Response 1 failed to provide two examples of the same category (it provided one valid rejected and one invalid accepted, instead of two of…
Write a regex that validates UK postcodes, give a one-line explanation of each part, and then state two valid UK postcodes your regex would reject or two invalid ones it would accept. Do not claim it is perfect.
# UK Postcode Regex ```regex ^([A-Z]{1,2}\d[A-Z\d]?) ?(\d[A-Z]{2})$ ``` *(Typically used with case-insensitive flag, or add `[A-Za-z]` explicitly)* ## Explanation | Part | Meaning | |---|---| | `^` | Start of string | | `([A-Z]{1,2}` | 1–2 letters for the postcode area (e.g., `SW`, `M`, `EC`) | | `\d` | A mandatory digit for the district number | | `[A-Z\d]?)` | An optional letter or digit (covers cases like `W1A`, `EC1A`) | | ` ?` | An optional space separating outward and inward codes | | `(\d` | Mandatory digit starting the inward code (the sector) | | `[A-Z]{2})` | Exactly two letters for the unit | | `$` | End of string | ## Known Limitations **This regex is not perfect** — it validates *structural format* only, not whether a postcode actually exists (that requires checking against Royal Mail's live database, since valid letter/number combinations are restricted in ways this pattern doesn't encode). Specific examples: - **Valid postcode it would incorrectly reject:** `GIR 0AA` (the special postcode for the Girobank, a legacy valid UK postcode) — it doesn't fit the standard area+district pattern. - **Invalid postcode it would incorrectly accept:** `ZZ99 9ZZ` — this matches the structural pattern perfectly but isn't a real allocated postcode (some `Z`-prefixed and `9`-suffixed combinations are reserved/unused in practice). For production use, cross-referencing against an official postcode database (like Royal Mail's PAF) is strongly recommended for true validation.
Here’s a commonly used **format-checking** regex for UK postcodes (not a full database validation): ```regex ^(GIR ?0AA|(?:[A-PR-UWYZ][0-9][0-9]?|[A-PR-UWYZ][A-HK-Y][0-9][0-9]?|[A-PR-UWYZ][0-9][A-HJKSTUW]|[A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY]) ?[0-9][ABD-HJLNP-UW-Z]{2})$ ``` ### One-line explanation of each part - `^` — start of string (so nothing extra can appear before the postcode). - `(GIR ?0AA| ... )` — allow either the special postcode `GIR 0AA` or a normal postcode pattern. - `[A-PR-UWYZ][0-9][0-9]?` — outward code in `A9` or `A99` form. - `[A-PR-UWYZ][A-HK-Y][0-9][0-9]?` — outward code in `AA9` or `AA99` form. - `[A-PR-UWYZ][0-9][A-HJKSTUW]` — outward code in `A9A` form. - `[A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY]` — outward code in `AA9A` form. - ` ?` — optional single space between outward and inward parts. - `[0-9]` — inward code must start with a digit. - `[ABD-HJLNP-UW-Z]{2}` — inward code must end with two letters from the allowed set. - `$` — end of string (so nothing extra can appear after the postcode). ### Two invalid postcodes this regex would still accept (Invalid in practice because they are unallocated/nonexistent, even though the format matches.) - `AA1 1AA` - `VX1 1AA` So: good for **syntax**, but not perfect for real-world validity.
google/gemini-3.1-pro-preview: B — Response 1 failed to provide two examples of the same category (it provided one valid rejected and one invalid accepted, instead of two of either). Response 2 followed all instructions perfectly and provided a more robust regex. / Response 1 followed all instructions perfectly, providing two invalid postcodes it would accept. Response 2 failed the constraint by providing one valid rejected and one invalid accepted, rather than two of the same category as requested. | x-ai/grok-4.5: B — Resp2 has superior accurate regex covering specials/outward forms, exact one-line parts, and two invalid accepts as required. Resp1 simpler/incomplete regex and mixed examples. / R1 fully meets structure with accurate complex regex, true one-line parts, and exactly two invalid accepts; no perfection claim. R2 simpler/less precise regex, table not one-liners, and mixed examples instead of two of one type. | deepseek/deepseek-v4-pro: B — Response 1's regex is simpler and accurate, explanation clear. It gave one valid rejected and one invalid accepted, violating the instruction to give two of one type. Response 2's regex has letter-set inaccuracies and mislabels VX1 1AA as accepted when it would be rejected. So 1 wins. / Response 1 regex is comprehensive and mostly correct, explanations clear, but example VX1 1AA is erroneous. Response 2 regex too simplistic, only partially followed instruction (gave one valid reject and one invalid accept instead of two of one type), so violated constraint.
tieCallback to asyncCoding · 8.8 v 9.2Both responses correctly refactor the code using promisify and async/await, ensuring errors are propagated. Response 2 is slightly more concise…
Refactor this to async/await with correct error propagation. Errors must not be swallowed. getUser(id, (e, user) => { if (e) return cb(e); getOrders(user.id, (e2, orders) => { if (e2) return cb(e2); getTotals(orders, (e3, totals) => cb(e3, totals)); }); });
## Refactored with async/await ```js const { promisify } = require('util'); const getUserAsync = promisify(getUser); const getOrdersAsync = promisify(getOrders); const getTotalsAsync = promisify(getTotals); async function getUserOrderTotals(id) { const user = await getUserAsync(id); const orders = await getOrdersAsync(user.id); return getTotalsAsync(orders); // returns a promise, propagates naturally } ``` **Usage (promise-based, preferred):** ```js getUserOrderTotals(id) .then(totals => cb(null, totals)) .catch(err => cb(err)); ``` **Or, if you need a caller who awaits it directly:** ```js async function handler(id) { try { const totals = await getUserOrderTotals(id); // use totals } catch (err) { // handle/log/rethrow — never swallow throw err; } } ``` ### Why this preserves correct error propagation - `promisify` turns each `(err, result)` callback into a rejected/resolved Promise — no error is lost in translation. - `await` on a rejected promise **throws synchronously inside the `async` function**, so a single `try/catch` (or the caller's `.catch`) catches failures from *any* of the three calls — `getUser`, `getOrders`, or `getTotals` — with no separate error-branch per step. - There's no `if (err) return cb(err)` boilerplate to accidentally miss, and no branch where an error is silently dropped or where a value is passed alongside an error (as the original `cb(e3, totals)` line risked). - If you still need callback-style compatibility for legacy callers, wrap once at the boundary: ```js function getUserOrderTotalsCb(id, cb) { getUserOrderTotals(id) .then(totals => cb(null, totals)) .catch(cb); // err becomes cb(err) } ``` This keeps all error handling centralized and explicit — every failure path terminates in `cb(err)` (or a rejected promise), never in silence.
Here’s a clean async/await refactor with **proper error propagation** (no swallowed errors). ```js const { promisify } = require('node:util'); const getUserAsync = promisify(getUser); const getOrdersAsync = promisify(getOrders); const getTotalsAsync = promisify(getTotals); async function getUserTotals(id) { // No try/catch needed here unless you transform errors. // If any await fails, the Promise rejects automatically. const user = await getUserAsync(id); const orders = await getOrdersAsync(user.id); const totals = await getTotalsAsync(orders); return totals; } ``` If you still need to expose a callback API (`cb`), wrap it like this: ```js function getUserTotalsCb(id, cb) { getUserTotals(id) .then(totals => cb(null, totals)) .catch(err => cb(err)); // propagate, don't swallow } ``` This preserves original behavior while using async/await internally.
google/gemini-3.1-pro-preview: B — Both responses correctly refactor the code using promisify and async/await, ensuring errors are propagated. Response 2 is slightly more concise and clearer for a quick read. / Both responses correctly refactor the code using util.promisify and async/await, ensuring errors are propagated. Response 1 is slightly more concise and straightforward, making it easier to digest. | x-ai/grok-4.5: tie (flipped on swap) — Both correctly promisify and await with natural rejection propagation, no swallowing. R2 is tighter, clearer, and equally complete for a busy pro; R1 adds extra verbosity without gain. / Both correctly refactor to async/await via promisify with proper rejection propagation and no swallowing. R2 adds clearer usage examples and explanation without excess length. | deepseek/deepseek-v4-pro: tie — Both correctly refactor to async/await using promisify, propagate errors via promise rejection, and provide callback wrappers. Response 1 explains more thoroughly; Response 2 is more concise. Both are accurate and useful. / Both responses correctly refactor to async/await using promisify, ensure errors propagate via rejected promises, and provide callback wrappers. Response 2 adds more explanation, but both are accurate and useful. No constraint violations.
tieDiagnose from a traceCoding · 8.5 v 8.8Response 2 is more concise and directly answers the prompt without unnecessary technical deep dives, making it better suited for a busy…
Given this Node stack trace, state the most likely root cause and the first thing you would check: TypeError: Cannot read properties of undefined (reading 'map') at renderRows (/app/src/table.js:42:19) at Table (/app/src/table.js:12:5) at renderWithHooks (/app/node_modules/react-dom/cjs/react-dom.development.js:16305:18) The component works in dev and fails only on the production build's first paint.
## Most Likely Root Cause The `Table` component is calling `.map()` on a prop (likely `rows` or `data`) **before that data has been fetched/loaded**. In dev, this bug is masked by one of the following: - Slower dev server / hot-reload timing that happens to let data arrive before first render - Dev mode double-rendering (React 18 Strict Mode) that "accidentally" re-renders after data loads, hiding the initial undefined state - Different bundling/minification behavior in prod causing a real timing difference in when the component first mounts vs. when data resolves In production, the first paint happens **before the async data (API call, Redux store hydration, context, etc.) resolves**, so the prop is genuinely `undefined` on that first render — and there's no default value or guard to handle it. ## First Thing to Check Look at line 42 of `table.js` and the `Table` component's prop signature (line 12) for something like: ```js function renderRows(rows) { return rows.map(...) // <-- line 42, no guard } function Table({ rows }) { return renderRows(rows); // rows is undefined on first render } ``` **Check specifically:** 1. **Where `rows`/`data` comes from** — is it from `props`, a `useState` initialized to `undefined`, a `useSelector`/`useContext`, or an async fetch? 2. **Whether there's a default value** — e.g., `useState([])` vs `useState()`, or missing `defaultProps`/destructuring default (`{ rows = [] }`). 3. **Whether there's a loading/undefined guard** before calling `.map()`. ## Quick Fix ```js function renderRows(rows = []) { return rows.map(...); } ``` or better, guard at the call site / add a loading state: ```js if (!rows) return <LoadingSpinner />; ``` The dev/prod discrepancy is a symptom of **race condition + missing null-check**, not a build config issue — verify data flow timing first, then patch with a default/guard.
Most likely root cause: **`renderRows` is calling `.map()` on a value that is `undefined` during the initial render** (probably a prop/state like `rows`, `data`, etc. that is filled asynchronously). Because it only fails on **production first paint**, this strongly suggests a **timing/initial-state issue** (data not ready yet) rather than React itself. ### First thing I’d check At `/app/src/table.js` line 42, inspect what value is being mapped and verify its initial value on first render in prod. Example check: - Is it initialized as `[]` (good) or `undefined` (bad)? - Are props defaulted? (`rows = []`) - Is render guarded? (`Array.isArray(rows) ? rows.map(...) : null`) In short: **ensure the mapped variable is always an array before first paint.**
google/gemini-3.1-pro-preview: B — Response 2 is more concise and directly answers the prompt without unnecessary technical deep dives, making it better suited for a busy professional while remaining completely accurate. / Response 1 is more concise and directly answers the prompt without unnecessary elaboration, making it highly useful for a busy non-technical professional. Response 2 is also accurate but slightly verbose. | x-ai/grok-4.5: tie (flipped on swap) — Both correctly identify undefined data on first paint as root cause and check line 42/defaults. R2 is clearer/more concise for a busy pro; R1 adds useful detail but is longer/more technical. / Both correctly identify undefined data on .map() at first prod paint due to missing init/guard. R2 adds stronger dev/prod timing analysis, code examples, and fix, making it more complete and useful while remaining clear. | deepseek/deepseek-v4-pro: tie (flipped on swap) — Response 2 is more concise and uses simpler language, better suited for a busy non-technical professional. Both are correct, but Response 1's extra detail may overwhelm. / Response 2 thoroughly explains root cause, including why dev masks it, and gives a clear check list with a fix. Response 1 is correct but less comprehensive. Both are helpful, but 2 is slightly more useful for understanding the discrepancy.
GitHub CopilotIdempotencyCoding · 6.7 v 9.2Both responses follow all instructions and constraints perfectly. Response 1 is slightly better because the failure mode it identifies…
Design an idempotent POST /payments endpoint so a client retry cannot charge twice. Describe the key, where it is stored, what happens on a concurrent duplicate, and what you return the second time. Maximum 200 words. Name the failure mode your design still has.
# Idempotent POST /payments **Key:** Client generates an `Idempotency-Key` (UUIDv4) header per unique payment attempt, reused verbatim on retries. **Storage:** A dedicated `idempotency_keys` table: `key (PK), request_hash, status (pending/completed/failed), response_body, response_code, created_at`. Written in the **same DB transaction** as the payment record. **Flow:** 1. On request, attempt `INSERT ... ON CONFLICT DO NOTHING` (or `SELECT FOR UPDATE`) on the key. 2. If insert succeeds → process payment, store result, commit. 3. If key exists with `status=completed` → return stored response immediately, skip charging. 4. If key exists with `status=pending` (concurrent duplicate) → either block briefly (short poll/lock wait) or return `409 Conflict` / `202` telling client to retry — never proceed to charge. 5. Also validate `request_hash` matches; mismatched payload with same key → `422`. **Second-time response:** Identical `200`/`201` body and status code as the original successful call — same payment ID, same amount — so the client sees a normal success, not an error. **Remaining failure mode:** If the process crashes *after* charging the payment gateway but *before* committing the local record (or before the gateway webhook confirms), the key stays "pending" or is lost, and a retry can still trigger a duplicate charge — solved only by reconciling against the gateway's own idempotency key.
Use an **Idempotency-Key** header (UUID from client), scoped to `(merchant_id, endpoint, key)`. 1. **On request start**: in a durable DB (same region as payments), `INSERT` into `idempotency` table with unique index on that scope: - key, request hash (amount/currency/order), status=`IN_PROGRESS`, created_at. 2. If insert succeeds, process the charge, then update row with: - status code, full response body, payment_id, status=`COMPLETED`. 3. If insert conflicts (duplicate key): - If request hash differs, return `409 Conflict` (“key reused with different payload”). - If existing row is `IN_PROGRESS` (concurrent duplicate), return `202 Accepted` (or block briefly, then return cached result when done). - If `COMPLETED`, return the **exact same status/body** as original (e.g., `201` with same `payment_id`). This prevents double charge on retries/concurrent submits because only one request can own the key. **Remaining failure mode:** if idempotency records are expired/deleted too soon (TTL) and client retries later with same business intent, it can be charged again.
google/gemini-3.1-pro-preview: A — Both responses follow all instructions and constraints perfectly. Response 1 is slightly better because the failure mode it identifies (distributed transaction failure with an external gateway) is a more fundamental and critical issue in payment systems than TTL expiration. / Both responses follow all instructions and constraints perfectly. Response 2 provides a more insightful and realistic failure mode (distributed transaction crash) compared to Response 1's TTL expiration, making it slightly more useful for a technical design. | x-ai/grok-4.5: B — R1 exceeds 200 words (~280); solid design else. R2 under limit, covers key/storage/concurrent/return/failure clearly and usefully. / Both cover key/storage/concurrent/return/failure well. R1 under 200 words, clear, useful. R2 exceeds 200 words (~250), slightly more complete failure mode but violates limit. | deepseek/deepseek-v4-pro: B — Both responses accurately describe idempotent key, storage, concurrent handling, second-time response, and a failure mode. Response 1 exceeds the 200-word limit (201 words), but content quality is equal. Tie. / Both designs accurately cover key, storage, concurrent duplicate, second return, and failure mode. Response 1 is concise; Response 2 exceeds the 200-word limit (206 words).
What they cost
- plan individual$16/mocursor.com/pricing23 Jul 2026stale
- plan pro$20/mocursor.com/pricing8 Aug 2026verified
- plan pro plus$60/mocursor.com/pricing8 Aug 2026verified
- plan teams$40/mocursor.com/pricing8 Aug 2026verified
- plan ultra$200/mocursor.com/pricing8 Aug 2026verified
- plan business$19/mogithub.com/features/copilot/plans8 Aug 2026verified
- plan pro$10/mogithub.com/features/copilot/plans8 Aug 2026verified
- plan pro plus$39/mogithub.com/features/copilot/plans8 Aug 2026verified
Speed, measured
Median response time across all 18 tasks: 9.6s (Cursor) vs 5.3s (GitHub Copilot).
Receipts — every citation, raw outputs
The full run — prompts, both raw outputs per task, judge reasoning, latencies, token counts and per-call cost — is downloadable as JSON. The task suite is versioned (suite-2026-08-skills/coding) and published on the methodology page.
- judge panelgoogle/gemini-3.1-pro-preview, x-ai/grok-4.5, deepseek/deepseek-v4-proour run (raw outputs)7 Aug 2026verified
- judge swap agreement0.833our run (raw outputs)7 Aug 2026verified
- judge swap kappa0.71our run (raw outputs)7 Aug 2026verified
- median latency ms a9581our run (raw outputs)7 Aug 2026verified
- median latency ms b5339our run (raw outputs)7 Aug 2026verified
- panel swap flip rate0.352our run (raw outputs)7 Aug 2026verified
- panel unanimous rate0.222our run (raw outputs)7 Aug 2026verified
- run cost a usd0.1618our run (raw outputs)7 Aug 2026verified
- run cost b usd0.1492our run (raw outputs)7 Aug 2026verified
- score a1our run (raw outputs)7 Aug 2026verified
- score b4our run (raw outputs)7 Aug 2026verified
- suite winscoding: a 1/b 4/tie 13our run (raw outputs)7 Aug 2026verified
- tasks total18our run (raw outputs)7 Aug 2026verified
- ties13our run (raw outputs)7 Aug 2026verified
GitHub Copilot took 4 of the 5 tasks that had a clear winner (Cursor 1, GitHub Copilot 4). The judge could pick a winner on 5 of 18 tasks; on the other 13 it could not tell them apart. That is a lean, not a proven win — at this sample size we cannot rule out chance, so we are not calling it decisive.