| 222 | } |
| 223 | |
| 224 | async function loadState(config: RunnerConfig): Promise<RunnerStateFile> { |
| 225 | if (!config.resume) { |
| 226 | return { |
| 227 | version: STATE_FILE_VERSION, |
| 228 | selectionSignature: config.selectionSignature, |
| 229 | datasetPath: config.datasetPath, |
| 230 | projectDirectory: config.projectDirectory, |
| 231 | createdAt: nowIso(), |
| 232 | updatedAt: nowIso(), |
| 233 | completedQuestionIds: [], |
| 234 | inProgress: {}, |
| 235 | }; |
| 236 | } |
| 237 | |
| 238 | try { |
| 239 | const content = await readFile(config.paths.stateFile, "utf8"); |
| 240 | const parsed = JSON.parse(content) as RunnerStateFile; |
| 241 | if (parsed.selectionSignature !== config.selectionSignature) { |
| 242 | throw new Error( |
| 243 | `Resume state does not match current selection. Existing selectionSignature=${parsed.selectionSignature}`, |
| 244 | ); |
| 245 | } |
| 246 | return parsed; |
| 247 | } catch (error) { |
| 248 | const typed = asError(error); |
| 249 | if ((typed as NodeJS.ErrnoException).code === "ENOENT") { |
| 250 | return { |
| 251 | version: STATE_FILE_VERSION, |
| 252 | selectionSignature: config.selectionSignature, |
| 253 | datasetPath: config.datasetPath, |
| 254 | projectDirectory: config.projectDirectory, |
| 255 | createdAt: nowIso(), |
| 256 | updatedAt: nowIso(), |
| 257 | completedQuestionIds: [], |
| 258 | inProgress: {}, |
| 259 | }; |
| 260 | } |
| 261 | throw typed; |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | async function persistState(config: RunnerConfig, state: RunnerStateFile): Promise<void> { |
| 266 | state.updatedAt = nowIso(); |