open-local-audit 0.63.0 → 0.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -104,9 +104,12 @@ export function renderWorkflowPreflightJson(report) {
104
104
  return `${JSON.stringify(report, null, 2)}\n`;
105
105
  }
106
106
  export async function runWorkflowPreflight(configPath) {
107
- return runWorkflowPreflightWithDependencies(configPath);
107
+ return (await runWorkflowPreflightEvaluationWithDependencies(configPath)).report;
108
108
  }
109
109
  export async function runWorkflowPreflightWithDependencies(configPath, overrides = {}) {
110
+ return (await runWorkflowPreflightEvaluationWithDependencies(configPath, overrides)).report;
111
+ }
112
+ export async function runWorkflowPreflightEvaluationWithDependencies(configPath, overrides = {}) {
110
113
  const dependencies = { ...defaultDependencies, ...overrides };
111
114
  let config;
112
115
  try {
@@ -117,15 +120,17 @@ export async function runWorkflowPreflightWithDependencies(configPath, overrides
117
120
  throw error;
118
121
  }
119
122
  return {
120
- version: 1,
121
- status: "blocked",
122
- checks: [
123
- {
124
- id: "configuration",
125
- status: "fail",
126
- message: "Workflow configuration could not be read or validated"
127
- }
128
- ]
123
+ report: {
124
+ version: 1,
125
+ status: "blocked",
126
+ checks: [
127
+ {
128
+ id: "configuration",
129
+ status: "fail",
130
+ message: "Workflow configuration could not be read or validated"
131
+ }
132
+ ]
133
+ }
129
134
  };
130
135
  }
131
136
  const checks = [
@@ -191,15 +196,18 @@ export async function runWorkflowPreflightWithDependencies(configPath, overrides
191
196
  ...(config.packageReports ? ["packaging"] : [])
192
197
  ];
193
198
  return {
194
- version: 1,
195
- status: reportStatus(checks),
196
- checks,
197
- provider: config.discovery.provider,
198
- stages,
199
- outputs: { outDir: config.outDir, ...config.paths },
200
- limits: {
201
- maxCandidates: config.discovery.provider === "google-places" ? config.discovery.limit : null,
202
- maxAudits: config.discovery.maxAudits ?? null
199
+ config,
200
+ report: {
201
+ version: 1,
202
+ status: reportStatus(checks),
203
+ checks,
204
+ provider: config.discovery.provider,
205
+ stages,
206
+ outputs: { outDir: config.outDir, ...config.paths },
207
+ limits: {
208
+ maxCandidates: config.discovery.provider === "google-places" ? config.discovery.limit : null,
209
+ maxAudits: config.discovery.maxAudits ?? null
210
+ }
203
211
  }
204
212
  };
205
213
  }
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-preflight.js","sourceRoot":"","sources":["../src/workflow-preflight.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workflow-preflight.js","sourceRoot":"","sources":["../src/workflow-preflight.ts"],"names":[],"mappings":"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"}
@@ -76,10 +76,13 @@ export interface WorkflowDependencies {
76
76
  packageReport: (options: Parameters<typeof packageReport>[0]) => Promise<ReportPackResult>;
77
77
  resolveGoogleMapsApiKey: typeof resolveGoogleMapsApiKey;
78
78
  }
79
+ export interface WorkflowRunOptions {
80
+ resume?: boolean;
81
+ }
79
82
  export declare class WorkflowRunError extends Error {
80
83
  readonly summary: WorkflowSummary;
81
84
  constructor(summary: WorkflowSummary);
82
85
  }
83
86
  export declare function safeLeadSlug(leadKey: string, companyName: string): string;
84
- export declare function runWorkflow(configPath: string, dependencies?: Partial<WorkflowDependencies>): Promise<WorkflowSummary>;
85
- export declare function runResolvedWorkflow(config: ResolvedWorkflowConfig, dependencies?: Partial<WorkflowDependencies>): Promise<WorkflowSummary>;
87
+ export declare function runWorkflow(configPath: string, dependencies?: Partial<WorkflowDependencies>, options?: WorkflowRunOptions): Promise<WorkflowSummary>;
88
+ export declare function runResolvedWorkflow(config: ResolvedWorkflowConfig, dependencies?: Partial<WorkflowDependencies>, options?: WorkflowRunOptions): Promise<WorkflowSummary>;
package/dist/workflow.js CHANGED
@@ -1,5 +1,5 @@
1
- import { randomUUID } from "node:crypto";
2
- import { lstat, mkdir, mkdtemp, realpath, rename, rm } from "node:fs/promises";
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { lstat, mkdir, mkdtemp, readFile, realpath, rename, rm } from "node:fs/promises";
3
3
  import { dirname, isAbsolute, join, relative, resolve } from "node:path";
4
4
  import { runDiscovery } from "./discovery-runner.js";
5
5
  import { packageReport } from "./report-pack.js";
@@ -101,6 +101,323 @@ async function writePrettyJson(path, value) {
101
101
  async function writeWorkflowSummary(summary) {
102
102
  await writePrettyJson(summary.outputs.workflowSummaryJson, summary);
103
103
  }
104
+ function stableJson(value) {
105
+ if (Array.isArray(value)) {
106
+ return `[${value.map(stableJson).join(",")}]`;
107
+ }
108
+ if (value && typeof value === "object") {
109
+ return `{${Object.entries(value)
110
+ .sort(([left], [right]) => left.localeCompare(right))
111
+ .map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`)
112
+ .join(",")}}`;
113
+ }
114
+ return JSON.stringify(value);
115
+ }
116
+ function workflowConfigFingerprint(config) {
117
+ const { paths: _paths, ...effectiveConfig } = config;
118
+ return createHash("sha256").update(stableJson(effectiveConfig)).digest("hex");
119
+ }
120
+ function workflowCheckpointPath(config) {
121
+ return join(config.outDir, "workflow-checkpoint.json");
122
+ }
123
+ function isRegularFile(info) {
124
+ return info.isFile() && !info.isSymbolicLink();
125
+ }
126
+ async function hashManagedFile(path) {
127
+ try {
128
+ const info = await lstat(path);
129
+ if (!isRegularFile(info)) {
130
+ throw new Error("Workflow checkpoint managed artifact must be a regular file");
131
+ }
132
+ return createHash("sha256").update(await readFile(path)).digest("hex");
133
+ }
134
+ catch (error) {
135
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
136
+ return undefined;
137
+ }
138
+ throw error;
139
+ }
140
+ }
141
+ function isMissingPath(error) {
142
+ return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
143
+ }
144
+ function packageSourceIntegrityId(index, fileName) {
145
+ return `package-source-${index}-${fileName}`;
146
+ }
147
+ function isPackageSourceIntegrityId(id, shortlistLeads) {
148
+ const match = /^package-source-(\d+)-(.+)$/.exec(id);
149
+ if (!match) {
150
+ return false;
151
+ }
152
+ const index = Number(match[1]);
153
+ return (Number.isSafeInteger(index) &&
154
+ index >= 0 &&
155
+ index < shortlistLeads.length &&
156
+ shortlistLeads[index].reportPath.trim().length > 0 &&
157
+ packageReportFileNames.includes(match[2]));
158
+ }
159
+ async function validateCheckpointPackageSources(config, checkpoint) {
160
+ if (!config.packageReports || checkpoint.summary.stages.packaging.status === "success") {
161
+ return;
162
+ }
163
+ for (const [index, lead] of checkpoint.shortlistLeads.entries()) {
164
+ if (!lead.reportPath.trim()) {
165
+ continue;
166
+ }
167
+ const source = await resolvePackageInputDir(config.paths.reportsDir, lead.reportPath);
168
+ await validatePackageSourceFiles(source.inputDir, source.realInputDir);
169
+ for (const fileName of packageReportFileNames) {
170
+ const id = packageSourceIntegrityId(index, fileName);
171
+ const expectedHash = checkpoint.integrity[id];
172
+ const actualHash = await hashManagedFile(join(source.inputDir, fileName));
173
+ if ((actualHash && !expectedHash) || (expectedHash && actualHash !== expectedHash)) {
174
+ throw new Error("Workflow checkpoint managed artifacts do not match");
175
+ }
176
+ }
177
+ }
178
+ }
179
+ async function addPackageSourceIntegrity(config, summary, shortlistLeads, integrity) {
180
+ if (!config.packageReports || summary.stages.shortlist.status !== "success") {
181
+ return;
182
+ }
183
+ for (const [index, lead] of shortlistLeads.entries()) {
184
+ if (!lead.reportPath.trim()) {
185
+ continue;
186
+ }
187
+ let source;
188
+ try {
189
+ source = await resolvePackageInputDir(config.paths.reportsDir, lead.reportPath);
190
+ }
191
+ catch (error) {
192
+ if (isMissingPath(error)) {
193
+ throw new Error("Checkpoint package source report is missing");
194
+ }
195
+ throw error;
196
+ }
197
+ await validatePackageSourceFiles(source.inputDir, source.realInputDir);
198
+ for (const fileName of packageReportFileNames) {
199
+ const hash = await hashManagedFile(join(source.inputDir, fileName));
200
+ if (!hash && fileName === "open-local-audit-report.json") {
201
+ throw new Error("Checkpoint package source report is missing");
202
+ }
203
+ if (hash) {
204
+ integrity[packageSourceIntegrityId(index, fileName)] = hash;
205
+ }
206
+ }
207
+ }
208
+ }
209
+ function checkpointArtifactPaths(config, summary) {
210
+ const paths = {};
211
+ if (summary.stages.discovery.status === "success") {
212
+ paths.leadsCsv = config.paths.leadsCsv;
213
+ paths.discoverySummaryJson = config.paths.discoverySummaryJson;
214
+ }
215
+ if (summary.stages.shortlist.status === "success") {
216
+ paths.shortlistCsv = config.paths.shortlistCsv;
217
+ paths.shortlistSummaryJson = config.paths.shortlistSummaryJson;
218
+ }
219
+ if (summary.stages.review.status === "success") {
220
+ paths.reviewSummaryJson = config.paths.reviewSummaryJson;
221
+ }
222
+ return paths;
223
+ }
224
+ async function createCheckpoint(config, summary, shortlistLeads) {
225
+ const integrity = {};
226
+ for (const [id, path] of Object.entries(checkpointArtifactPaths(config, summary))) {
227
+ const hash = await hashManagedFile(path);
228
+ if (hash) {
229
+ integrity[id] = hash;
230
+ }
231
+ }
232
+ await addPackageSourceIntegrity(config, summary, shortlistLeads, integrity);
233
+ return {
234
+ version: 1,
235
+ configFingerprint: workflowConfigFingerprint(config),
236
+ summary,
237
+ integrity,
238
+ shortlistLeads
239
+ };
240
+ }
241
+ async function writeWorkflowCheckpoint(config, summary, shortlistLeads) {
242
+ await writePrettyJson(workflowCheckpointPath(config), await createCheckpoint(config, summary, shortlistLeads));
243
+ }
244
+ function isRecord(value) {
245
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
246
+ }
247
+ function hasOnlyKeys(value, keys) {
248
+ return Object.keys(value).every((key) => keys.includes(key));
249
+ }
250
+ function isCount(value) {
251
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
252
+ }
253
+ function isStageSummary(value, keys) {
254
+ return (isRecord(value) &&
255
+ hasOnlyKeys(value, ["status", ...keys]) &&
256
+ typeof value.status === "string" &&
257
+ keys.every((key) => value[key] === undefined || isCount(value[key])));
258
+ }
259
+ function isShortlistLeadSnapshot(value) {
260
+ if (!isRecord(value)) {
261
+ return false;
262
+ }
263
+ const stringKeys = [
264
+ "companyName",
265
+ "website",
266
+ "segment",
267
+ "profile",
268
+ "priority",
269
+ "auditStatus",
270
+ "hasWebsite",
271
+ "source",
272
+ "topFinding",
273
+ "contactConfidence",
274
+ "preferredContactChannel",
275
+ "contactabilityReason",
276
+ "reason",
277
+ "reportPath",
278
+ "leadKey",
279
+ "reviewStatus",
280
+ "reviewReason",
281
+ "lastReviewedAt"
282
+ ];
283
+ return (hasOnlyKeys(value, ["rank", ...stringKeys, "score", "opportunityScore"]) &&
284
+ typeof value.rank === "number" &&
285
+ Number.isSafeInteger(value.rank) &&
286
+ value.rank > 0 &&
287
+ stringKeys.every((key) => typeof value[key] === "string") &&
288
+ (value.score === undefined || typeof value.score === "number") &&
289
+ (value.opportunityScore === undefined || typeof value.opportunityScore === "number"));
290
+ }
291
+ function isCompletedPackageEntries(entries, shortlistLeads, packagesDir) {
292
+ if (entries.length !== shortlistLeads.length) {
293
+ return false;
294
+ }
295
+ const slugCounts = new Map();
296
+ return entries.every((entry, index) => {
297
+ if (!isRecord(entry)) {
298
+ return false;
299
+ }
300
+ const lead = shortlistLeads[index];
301
+ if (entry.leadKey !== lead.leadKey || entry.companyName !== lead.companyName) {
302
+ return false;
303
+ }
304
+ if (!lead.reportPath.trim()) {
305
+ return hasOnlyKeys(entry, ["leadKey", "companyName", "status"]) && entry.status === "skipped";
306
+ }
307
+ const slug = nextUniqueSlug(safeLeadSlug(lead.leadKey, lead.companyName), slugCounts);
308
+ return (hasOnlyKeys(entry, ["leadKey", "companyName", "status", "outDir"]) &&
309
+ entry.status === "packaged" &&
310
+ entry.outDir === join(packagesDir, slug));
311
+ });
312
+ }
313
+ function isWorkflowCheckpoint(value, config) {
314
+ if (!isRecord(value) || !hasOnlyKeys(value, ["version", "configFingerprint", "summary", "integrity", "shortlistLeads"])) {
315
+ return false;
316
+ }
317
+ const checkpoint = value;
318
+ const summary = checkpoint.summary;
319
+ if (!isRecord(summary) || !hasOnlyKeys(summary, ["version", "status", "stages", "outputs", "discoveredLeads", "selectedLeads", "packages"])) {
320
+ return false;
321
+ }
322
+ const stages = summary.stages;
323
+ const packages = summary.packages;
324
+ const integrity = checkpoint.integrity;
325
+ if (!isRecord(stages) ||
326
+ !hasOnlyKeys(stages, ["discovery", "shortlist", "review", "packaging"]) ||
327
+ !isRecord(packages) ||
328
+ !hasOnlyKeys(packages, ["packaged", "skipped", "failed", "entries"]) ||
329
+ !isRecord(integrity) ||
330
+ !isRecord(summary.outputs)) {
331
+ return false;
332
+ }
333
+ const stageSummaries = {
334
+ discovery: isStageSummary(stages.discovery, ["totalCandidates", "suppressedCandidates", "audited"]),
335
+ shortlist: isStageSummary(stages.shortlist, ["totalRows", "suppressedRows", "filteredRows", "selected"]),
336
+ review: isStageSummary(stages.review, ["totalRows", "reviewedRows", "actionableLeads", "staleRows", "invalidReviewedAtRows", "unreviewedRows"]),
337
+ packaging: isStageSummary(stages.packaging, ["packaged", "skipped", "failed"])
338
+ };
339
+ const expectedIntegrity = checkpointArtifactPaths(config, summary);
340
+ const expectedOutputEntries = Object.entries(config.paths);
341
+ const checkpointOutputs = summary.outputs;
342
+ const outputsMatch = hasOnlyKeys(checkpointOutputs, expectedOutputEntries.map(([key]) => key)) && expectedOutputEntries.every(([key, path]) => checkpointOutputs[key] === path);
343
+ const stageStatus = (name) => stages[name].status;
344
+ if (!Array.isArray(checkpoint.shortlistLeads)) {
345
+ return false;
346
+ }
347
+ const shortlistLeads = checkpoint.shortlistLeads;
348
+ const packagesValid = isCount(packages.packaged) &&
349
+ isCount(packages.skipped) &&
350
+ packages.failed === 0 &&
351
+ Array.isArray(packages.entries) &&
352
+ packages.entries.length === packages.packaged + packages.skipped;
353
+ const completedPackagesValid = stageStatus("packaging") !== "success" ||
354
+ packages.entries.filter((entry) => isRecord(entry) && entry.status === "packaged").length === packages.packaged &&
355
+ packages.entries.filter((entry) => isRecord(entry) && entry.status === "skipped").length === packages.skipped &&
356
+ isCompletedPackageEntries(packages.entries, shortlistLeads, config.paths.packagesDir);
357
+ const packagingCountersMatch = stageStatus("packaging") !== "success" ||
358
+ (stages.packaging.packaged === packages.packaged &&
359
+ stages.packaging.skipped === packages.skipped &&
360
+ stages.packaging.failed === packages.failed);
361
+ return (checkpoint.version === 1 &&
362
+ typeof checkpoint.configFingerprint === "string" &&
363
+ /^[a-f0-9]{64}$/.test(checkpoint.configFingerprint) &&
364
+ summary.version === 1 &&
365
+ summary.status === "success" &&
366
+ stageSummaries.discovery &&
367
+ stageSummaries.shortlist &&
368
+ stageSummaries.review &&
369
+ stageSummaries.packaging &&
370
+ outputsMatch &&
371
+ isCount(summary.discoveredLeads) &&
372
+ isCount(summary.selectedLeads) &&
373
+ packagesValid &&
374
+ completedPackagesValid &&
375
+ packagingCountersMatch &&
376
+ stageStatus("discovery") === "success" &&
377
+ (stageStatus("shortlist") === "success" || stageStatus("shortlist") === "not-run") &&
378
+ (config.review ? stageStatus("review") === "success" || stageStatus("review") === "not-run" : stageStatus("review") === "skipped") &&
379
+ (config.packageReports
380
+ ? stageStatus("packaging") === "success" || stageStatus("packaging") === "not-run"
381
+ : stageStatus("packaging") === "skipped") &&
382
+ (stageStatus("shortlist") === "success" ||
383
+ (stageStatus("review") !== "success" && stageStatus("packaging") !== "success" && summary.selectedLeads === 0 && shortlistLeads.length === 0)) &&
384
+ (stageStatus("review") !== "success" || stageStatus("shortlist") === "success") &&
385
+ (stageStatus("packaging") !== "success" ||
386
+ (stageStatus("shortlist") === "success" && (!config.review || stageStatus("review") === "success"))) &&
387
+ shortlistLeads.every(isShortlistLeadSnapshot) &&
388
+ (stageStatus("shortlist") !== "success" ||
389
+ (summary.selectedLeads === shortlistLeads.length && stages.shortlist.selected === shortlistLeads.length)) &&
390
+ Object.keys(integrity).every((id) => Object.hasOwn(expectedIntegrity, id) || isPackageSourceIntegrityId(id, shortlistLeads)) &&
391
+ Object.entries(expectedIntegrity).every(([id]) => typeof integrity[id] === "string" && /^[a-f0-9]{64}$/.test(integrity[id])) &&
392
+ Object.entries(integrity).every(([, hash]) => typeof hash === "string" && /^[a-f0-9]{64}$/.test(hash)) &&
393
+ (!config.packageReports ||
394
+ stageStatus("packaging") === "success" ||
395
+ shortlistLeads.every((lead, index) => !lead.reportPath.trim() ||
396
+ typeof integrity[packageSourceIntegrityId(index, "open-local-audit-report.json")] === "string")));
397
+ }
398
+ async function readWorkflowCheckpoint(config) {
399
+ let value;
400
+ try {
401
+ value = JSON.parse(await readFile(workflowCheckpointPath(config), "utf8"));
402
+ }
403
+ catch {
404
+ throw new Error("Workflow checkpoint is missing or invalid");
405
+ }
406
+ if (!isWorkflowCheckpoint(value, config)) {
407
+ throw new Error("Workflow checkpoint is missing or invalid");
408
+ }
409
+ if (value.configFingerprint !== workflowConfigFingerprint(config)) {
410
+ throw new Error("Workflow checkpoint does not match the current configuration");
411
+ }
412
+ for (const [id, path] of Object.entries(checkpointArtifactPaths(config, value.summary))) {
413
+ const expectedHash = value.integrity[id];
414
+ if (!expectedHash || (await hashManagedFile(path)) !== expectedHash) {
415
+ throw new Error("Workflow checkpoint managed artifacts do not match");
416
+ }
417
+ }
418
+ await validateCheckpointPackageSources(config, value);
419
+ return value;
420
+ }
104
421
  async function throwPersistedWorkflowFailure(summary) {
105
422
  try {
106
423
  await writeWorkflowSummary(summary);
@@ -236,63 +553,76 @@ function nextUniqueSlug(baseSlug, counts) {
236
553
  counts.set(baseSlug, nextCount);
237
554
  return nextCount === 1 ? baseSlug : `${baseSlug}-${nextCount}`;
238
555
  }
239
- export async function runWorkflow(configPath, dependencies = {}) {
556
+ export async function runWorkflow(configPath, dependencies = {}, options = {}) {
240
557
  const resolvedDependencies = {
241
558
  ...defaultDependencies,
242
559
  ...dependencies
243
560
  };
244
561
  const config = await resolvedDependencies.readWorkflowConfig(configPath);
245
- return await runResolvedWorkflow(config, resolvedDependencies);
562
+ return await runResolvedWorkflow(config, resolvedDependencies, options);
246
563
  }
247
- export async function runResolvedWorkflow(config, dependencies = {}) {
564
+ export async function runResolvedWorkflow(config, dependencies = {}, options = {}) {
248
565
  const resolvedDependencies = {
249
566
  ...defaultDependencies,
250
567
  ...dependencies
251
568
  };
569
+ const checkpoint = options.resume ? await readWorkflowCheckpoint(config) : undefined;
252
570
  await prepareWorkflowManagedDirectories(config);
253
- const summary = createInitialSummary(config);
571
+ const summary = checkpoint
572
+ ? { ...checkpoint.summary, outputs: config.paths, error: undefined, status: "success" }
573
+ : createInitialSummary(config);
574
+ let shortlistLeads = checkpoint?.shortlistLeads ?? [];
254
575
  const knownSecrets = [];
255
576
  try {
256
- const googleApiKey = config.discovery.provider === "google-places" ? resolvedDependencies.resolveGoogleMapsApiKey() : undefined;
257
- if (googleApiKey) {
258
- knownSecrets.push(googleApiKey);
577
+ if (summary.stages.discovery.status !== "success") {
578
+ const googleApiKey = config.discovery.provider === "google-places" ? resolvedDependencies.resolveGoogleMapsApiKey() : undefined;
579
+ if (googleApiKey) {
580
+ knownSecrets.push(googleApiKey);
581
+ }
582
+ const discoveryResult = await resolvedDependencies.runDiscovery({
583
+ provider: config.discovery.provider,
584
+ ...(config.discovery.provider === "manual-csv" ? { input: config.discovery.input } : {}),
585
+ ...(config.discovery.provider === "google-places" ? { query: config.discovery.query } : {}),
586
+ profile: config.discovery.profile,
587
+ outDir: config.paths.reportsDir,
588
+ managedOutputRoot: config.paths.reportsDir,
589
+ exportCsv: config.paths.leadsCsv,
590
+ summaryJson: config.paths.discoverySummaryJson,
591
+ reviewCsv: config.review?.csv,
592
+ dryRun: false,
593
+ concurrency: config.discovery.concurrency,
594
+ ...(config.discovery.maxAudits !== undefined ? { maxAudits: config.discovery.maxAudits } : {}),
595
+ ...(config.discovery.provider === "google-places" ? { limit: config.discovery.limit } : {}),
596
+ ...(googleApiKey !== undefined ? { apiKey: googleApiKey } : {})
597
+ });
598
+ updateDiscoveryStage(summary, discoveryResult);
599
+ await writeWorkflowCheckpoint(config, summary, shortlistLeads);
259
600
  }
260
- const discoveryResult = await resolvedDependencies.runDiscovery({
261
- provider: config.discovery.provider,
262
- ...(config.discovery.provider === "manual-csv" ? { input: config.discovery.input } : {}),
263
- ...(config.discovery.provider === "google-places" ? { query: config.discovery.query } : {}),
264
- profile: config.discovery.profile,
265
- outDir: config.paths.reportsDir,
266
- managedOutputRoot: config.paths.reportsDir,
267
- exportCsv: config.paths.leadsCsv,
268
- summaryJson: config.paths.discoverySummaryJson,
269
- reviewCsv: config.review?.csv,
270
- dryRun: false,
271
- concurrency: config.discovery.concurrency,
272
- ...(config.discovery.maxAudits !== undefined ? { maxAudits: config.discovery.maxAudits } : {}),
273
- ...(config.discovery.provider === "google-places" ? { limit: config.discovery.limit } : {}),
274
- ...(googleApiKey !== undefined ? { apiKey: googleApiKey } : {})
275
- });
276
- updateDiscoveryStage(summary, discoveryResult);
277
- const shortlistResult = await resolvedDependencies.runShortlistReport({
278
- input: config.paths.leadsCsv,
279
- out: config.paths.shortlistCsv,
280
- summaryJson: config.paths.shortlistSummaryJson,
281
- reviewCsv: config.review?.csv,
282
- format: "csv",
283
- shortlist: config.shortlist
284
- });
285
- updateShortlistStage(summary, shortlistResult);
286
- if (config.review) {
601
+ if (summary.stages.shortlist.status !== "success") {
602
+ const shortlistResult = await resolvedDependencies.runShortlistReport({
603
+ input: config.paths.leadsCsv,
604
+ out: config.paths.shortlistCsv,
605
+ summaryJson: config.paths.shortlistSummaryJson,
606
+ reviewCsv: config.review?.csv,
607
+ format: "csv",
608
+ shortlist: config.shortlist
609
+ });
610
+ updateShortlistStage(summary, shortlistResult);
611
+ shortlistLeads = shortlistResult.leads;
612
+ await writeWorkflowCheckpoint(config, summary, shortlistLeads);
613
+ }
614
+ if (config.review && summary.stages.review.status !== "success") {
287
615
  const reviewSummary = await resolvedDependencies.summarizeReviewCsvFile(config.review.csv, {
288
616
  staleBefore: config.review.staleBefore
289
617
  });
290
618
  await writePrettyJson(config.paths.reviewSummaryJson, reviewSummary);
291
619
  updateReviewStage(summary, reviewSummary);
620
+ await writeWorkflowCheckpoint(config, summary, shortlistLeads);
292
621
  }
293
- if (config.packageReports) {
622
+ if (config.packageReports && summary.stages.packaging.status !== "success") {
623
+ summary.packages = { packaged: 0, skipped: 0, failed: 0, entries: [] };
294
624
  const slugCounts = new Map();
295
- for (const lead of shortlistResult.leads) {
625
+ for (const lead of shortlistLeads) {
296
626
  const packageEntry = await packageLead(config.paths, lead, slugCounts, resolvedDependencies.packageReport, knownSecrets);
297
627
  summary.packages.entries.push(packageEntry);
298
628
  if (packageEntry.status === "packaged") {
@@ -314,6 +644,7 @@ export async function runResolvedWorkflow(config, dependencies = {}) {
314
644
  };
315
645
  await throwPersistedWorkflowFailure(summary);
316
646
  }
647
+ await writeWorkflowCheckpoint(config, summary, shortlistLeads);
317
648
  }
318
649
  }
319
650
  catch (error) {