How to visualize CSV as a workflow
A workflow is a process with rework in it. How to write a backward Line to reference in a CSV file, cap the retry so revisions cannot cycle forever, and tell a real loop from a mistyped row number.
A CSV file becomes a workflow rather than a list the moment one Line to cell holds a row number smaller than its own: that backward reference is the rework loop every approval process has and almost every export leaves out.
The short version
- A Line to number smaller than its own row is a loop back. Nothing else marks one.
- Loop rows are multi-target rows, so their cells hold commas and must be quoted in CSV.
- Cap the retry: a Decision that counts the attempt is what stops a loop cycling forever.
- The rework rows can sit anywhere in the file: the loop is the edge, not the position.
- A backward reference into a row that redoes no work is a typo, not a loop.
Where the loops hide in an exported file
CSV exports of a process come out of systems that record what happened: an approval log, a ticket export, the audit table behind a workflow engine. They are flat, one row per event, usually ordered by timestamp, and they are complete about the past and silent about the shape. Reading one as a workflow means answering a question the file does not contain: when this step is rejected, where does the work go back to?
In QueryChart the answer is a number. The Line to column holds the 1-based row numbers a row connects to, and nothing requires that number to be larger than the row it sits on. Row 16 pointing at row 3 is a loop: work returns to row 3 and comes forward again. That is the whole mechanism. There is no loop shape, no repeat marker and no separate return column: a workflow differs from a straight-line process by one number being smaller than expected.
CSV makes that one number harder to get right than a spreadsheet does, for a reason specific to the format. The rows that loop are the rows with more than one destination, so their Line to cell reads 3,17 and their Line text cell reads First revision,Second revision. Both contain commas, and a comma in a comma-delimited file has to be quoted or the row silently gains columns and slides out of alignment with the header. /guides/how-to-define-flowchart-connections-in-csv covers the pairing rules; this page is about what those cells mean once they parse.
The example below is a discount approval workflow, the same chart as /templates/csv-approval-workflow. It has three backward references in twenty-four rows: a revision that returns to the margin calculation, an escalation that returns to the director's summary, and a customer counter-offer that returns to the revision. Open the spreadsheet panel and read the Line to column against the row numbers.
Three backward references in twenty-four rows
A discount approval workflow at four stages of completeness. The spreadsheet panel under the canvas shows which columns are filled in: watch Line to for numbers smaller than the row they sit on.
Shapes, no connections
Rows 1 to 6 with the Shape column filled in and nothing else: a Start for "Receive a discount request from the customer", three Process rows, the Decision "Margin at or above 40%?", and the rep-level approval. Nothing points anywhere yet. This is roughly what a raw export gives you: a list with a type column bolted on, which is a table, not a workflow.
The forward path
Line to fills in and the approval ladder appears: row 5 forks to 6 or 7, row 7 to 8 or 10, row 10 to 11 or 13, each tier handing the margin band to a more senior approver. Every number in this prefix is larger than the row holding it, so all the arrows run forward and the file could still be read top to bottom as a list.
The number that points backwards
Rows 15 to 17 add the rework block, and row 16 is where the file stops being a list. "Second time back from the same approver?" carries Line to 3,17 with Line text First revision,Second revision: the 3 is smaller than 16, so a first revision returns to "Calculate gross margin after discount": a changed price is a changed margin and may land in a different band. The second revision goes to 17 instead, which is the cap.
The whole workflow with lanes
All twenty-four rows, with Quote, Margin check, Approval, Rework and Issue across the top and Sales rep, Sales manager, Finance and Commercial director down the side. The Rework column now visibly collects rows 15 to 17, which is why the loop is legible on the canvas and invisible in the export. The last backward reference sits on row 20, where "Asks for a bigger discount" sends the customer's counter back to row 15 rather than to a new quote.
How it works
Re-order the export into process order
System exports come out in timestamp or ticket order, which is the order things happened, not the order the process runs. Sort the rows into the sequence a single case travels before you write a single Line to value, because those values are row positions and re-sorting the file afterwards invalidates every one of them.
Check the header row survived the export
Columns are matched by header name, so the header row is the only part of the file that has to be exactly right. Confirm it reads Shape, Box text, Line to, Line text. A byte-order mark from a UTF-8 export attaches itself to the first header name, and a semicolon-delimited export from a European system puts the whole line into one cell: in both cases the column is not recognised and is carried through as an unused extra rather than failing loudly.
Write the forward path first
Fill in Line to for the straight-through case only, where every number is larger than the row it sits on. Get the workflow running end to end with no rework at all. Loops added to a path you have not yet verified are almost impossible to read back, because you cannot tell a return edge from a wrong number when both point at a row you are unsure of.
Add one rework row and point the return edge at it
Give the revision its own row ("Revise the discount, the price or the terms") and send every rejecting Decision to that one row rather than duplicating a rework step under each approver. Then write the return edge out of the rework block, pointing at the row where the changed input is re-evaluated. In the example that is row 3, the margin calculation, not row 1.
Cap the retry
An uncapped loop is a workflow that can run forever, and in a real process it does not. Add a Decision after the rework row that asks how many times this has come back (row 16, "Second time back from the same approver?") with Line to 3,17: round the loop once, escalate on the second pass. Quote that cell so the comma stays inside it.
Drop the file in and read the backward arrows
Drag the .csv onto the spreadsheet area of a new chart, or paste the text straight into it, and the diagram renders. Then read every arrow that runs backwards. Each one should be a case you can name out loud: a revision, an escalation, a counter-offer. An arrow you cannot name is a transposed row number, and this is the only view in which it is obvious.
Mistakes to avoid
The comma inside the Line to cell
A row with two destinations holds 3,17 and a row with three holds 19,15,22. Unquoted in a comma-delimited file those become three and four columns, every later column shifts left, and the misalignment lands on exactly the rows that carry the loops. Spreadsheets quote such cells by default on export; hand-written and script-generated files often do not.
Sorting the file after you numbered it
Line to values are row positions, not identifiers, so one sort of the sheet renumbers every row and leaves every reference pointing at whatever now occupies that position. The chart still renders and is entirely wrong. Settle the order first (see /guides/how-to-structure-csv-for-flowcharts) then number.
A loop that re-enters too early
Pointing the return edge at row 1 restarts intake, which claims the customer submitted a second request. Send it to the first row whose result actually changes because of the rework. In the example the revision returns to the margin calculation, because the price changed and the margin follows from the price.
No exit from the loop
A backward edge with no counter beside it makes a workflow that never terminates and hides the case everyone dreads: the deal that goes round three times and dies anyway. The cap is a Decision with a second branch out: row 16 goes to 17, and 17 either escalates to the director or ends at "Deal dropped on margin grounds".
Open the approval workflow example
Open the example as your own chart, then replace the rows with your own file and check that every backward number lands where the work is actually redone.
Related guides
Drawing quantities instead of steps?
A workflow shows steps. If your CSV is about how much flows between categories, a Sankey diagram is the better visualization.
Frequently asked questions
Can a Line to value point at an earlier row?
Yes, and that is the whole mechanism for rework. Line to takes the 1-based position of the target row and nothing constrains it to be larger than the row it sits on, so a 3 in row 16's cell draws an arrow from 16 back up to 3. The renderer routes it around the boxes; you do not place it. Because there is no separate loop marker, reading a CSV for its loops means scanning for numbers smaller than their own row: that is the only signal in the file.
How do I stop a rework loop from cycling forever?
Put a Decision on the return path that counts the attempt. In the example, row 16 asks "Second time back from the same approver?" and carries Line to 3,17 with Line text First revision,Second revision, so the first pass goes round and the second leaves the loop for row 17, "Hold the discount or walk away?". The count is not stored anywhere: the diagram documents the rule, and the rule is what people follow. What matters is that the loop has a labelled way out on the chart rather than an implicit one in someone's head.
How do I tell a genuine loop from a wrong row number in someone else's file?
Three tests. Does the target row redo work that the rework actually changed? A loop that returns to a step whose output cannot differ this time is a mistake. Does the backward edge leave a Decision? A Process row whose only Line to points backwards, with no forward exit, makes the rest of the file unreachable and is nearly always a transposition. And is the branch labelled? Real loops carry a Line text naming the case; typos never do, because nobody labels an edge they did not mean to draw.
My export has one row per approval event, not one per step. What do I do?
Collapse it. An audit table repeats the same step once per occurrence, so a deal that was revised twice appears as two "returned to requester" rows, and importing it directly produces a diagram of one case rather than a definition of the process. Pull out the distinct steps, keep one row each, and let the backward reference carry the repetition: that substitution is the point of the loop. /csv-workflow-generator has the shorter version of this if you just want to see the file rendered first.
Does the diagram stay readable when the loops cross the lanes?
Routing is computed rather than drawn, so a return edge from the Rework column back to the Quote column is routed around the boxes without you moving anything. What makes a looping workflow unreadable is not the arrows but the row order: keep the rework rows together, as rows 15 to 17 are in the example, instead of scattering a revision step under each approver. Three approvers with a rework row each produce three loops that are really one.