CSV export drops the last row and mangles names with commasNeeds reviewkordra/node-4813$0.42 / $3.0013low

CSV export drops the last row and mangles names with commas

A support customer's export was missing its final row and any name with a comma broke into extra columns. Fix the export and add a test so it can't regress.

Slack · #support · thread
Dispatched to fleet · Standard · 3 workers, diversified · cap $3.00 · runner mbp-maya.local
Kordra Fleet
installnpm cipassed
regressionnpm test -- export/csvpassed
repronpm test -- export/csv.repropassed
Best-of-3

Verified against the repro

1 of 3 candidates passed BOTH the failing-then-fixed repro and the existing suite; selected Worker B (node-4813). Worker A fixed the dropped row but left commas unescaped — repro still failed.

behavioral gateStandard$0.42 of $3.001 file · +13 5Low blast radius
UI walkthroughrecorded on mbp-maya.local
auto-captured on the runner

Open Reports with 1,204 rows loaded → Click Export CSV → Download completes — all rows present → Open in a spreadsheet: "Ada, Lovelace" stays in one column

The changekordra/node-4813
services/api/src/export/csv.ts+13 5
@@ -9,15 +9,27 @@ export function toCsv(rows: Row[], columns: Column[]): string {
99 const header = columns.map((c) => c.label).join(",");
1010 const lines = [header];
11 for (let i = 0; i < rows.length - 1; i++) {
12 const row = rows[i];
13 const cells = columns.map((c) => String(row[c.key] ?? ""));
14 lines.push(cells.join(","));
15 }
11 for (let i = 0; i < rows.length; i++) {
12 const row = rows[i];
13 const cells = columns.map((c) => csvField(row[c.key]));
14 lines.push(cells.join(","));
15 }
1616 return lines.join("\r\n");
1717}
18
19// RFC 4180: quote any field containing a comma, double-quote, CR or LF, and escape
20// embedded quotes by doubling them — so names like "Ada, Lovelace" stop spilling
21// into extra columns (see decision #142).
22function csvField(value: unknown): string {
23 const s = value == null ? "" : String(value);
24 return /[",\r\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
25}
1826
Tests
repropassednpm test -- export/csv.repro # fails on base, passes on the fix
testspassednpm test -- export/csv
Groundinggrounded in 1 org decision
▤ document
Data Export Format — internal spec

Section 3: line terminator is CRLF; header row required; UTF-8, no BOM.

◐ memory
Support thread: "my CSV opens as one big column in Excel"

Repro attached a 1,204-row export where row 1,204 was missing and a name broke parsing.

Proposed for the brainthe run wants 1 decision remembered — your call

Ship an F2P repro test with every data-export bug fix

Export bugs are silent and customer-visible. Require a failing-then-fixed test alongside the fix so the same corruption can't come back.

You are the gate. Approving pushes kordra/node-4813 and opens a PR — and ratifies 1 proposed decision. Never your default branch.
⌘⏎ to send · Enter for a new line