deepline 0.1.176 → 0.1.177

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.
package/dist/cli/index.js CHANGED
@@ -622,17 +622,18 @@ var SDK_RELEASE = {
622
622
  // 0.1.111 ships dataset-native tool list getters and result row datasets.
623
623
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
624
624
  // fields shipped in 0.1.153.
625
- version: "0.1.176",
625
+ // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
626
+ version: "0.1.177",
626
627
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
627
628
  supportPolicy: {
628
- latest: "0.1.176",
629
+ latest: "0.1.177",
629
630
  minimumSupported: "0.1.53",
630
631
  deprecatedBelow: "0.1.53",
631
632
  commandMinimumSupported: [
632
633
  {
633
634
  command: "enrich",
634
- minimumSupported: "0.1.154",
635
- reason: "Older SDK CLI enrich generated stale play source; SDK CLI enrich 0.1.153 generated removed StepOptions fields in play source."
635
+ minimumSupported: "0.1.175",
636
+ reason: "Older SDK CLI enrich generated stale play source; SDK CLI enrich 0.1.153 generated removed StepOptions fields in play source; SDK CLI enrich before 0.1.175 could silently succeed on the empty-waterfall bug when a requested waterfall returned no values for selected rows."
636
637
  },
637
638
  {
638
639
  command: "plays",
@@ -17839,14 +17840,39 @@ function enrichOutputJson(exportResult) {
17839
17840
  ...exportResult.partial ? { rows: exportResult.rows, partial: true } : {}
17840
17841
  };
17841
17842
  }
17843
+ function enrichReportJson(report) {
17844
+ if (!report) {
17845
+ return {};
17846
+ }
17847
+ return {
17848
+ ...report.jobs.length > 0 ? {
17849
+ failure_report: {
17850
+ path: report.path,
17851
+ jobs: report.jobs.length
17852
+ }
17853
+ } : {},
17854
+ ...report.issues.length > 0 ? {
17855
+ enrich_issues: {
17856
+ path: report.path,
17857
+ issues: report.issues.length,
17858
+ items: report.issues
17859
+ }
17860
+ } : {}
17861
+ };
17862
+ }
17842
17863
  async function buildEnrichFailureReport(input2) {
17864
+ const fallbackRowsInfo = input2.exportResult ? null : extractCanonicalRowsInfo(input2.status);
17843
17865
  return maybeEmitEnrichFailureReport({
17844
17866
  config: input2.config,
17845
17867
  rows: input2.rows,
17846
17868
  rowRange: input2.rowRange,
17847
17869
  client: input2.client,
17848
17870
  outputPath: input2.reportOutputPath ?? input2.exportResult?.path ?? input2.outputPath ?? null,
17849
- status: input2.status
17871
+ followUpOutputPath: input2.followUpOutputPath,
17872
+ status: input2.status,
17873
+ rowSetComplete: input2.exportResult ? !input2.exportResult.partial : Boolean(
17874
+ fallbackRowsInfo?.complete && input2.rows.length >= fallbackRowsInfo.totalRows
17875
+ )
17850
17876
  });
17851
17877
  }
17852
17878
  function recordField(value, key) {
@@ -18123,7 +18149,7 @@ function emitPlainBatchRunFailure(input2) {
18123
18149
  `Batch ${input2.chunkIndex + 1}/${input2.chunkCount} failed for rows ${input2.rows.rowStart}:${input2.rows.rowEnd}.
18124
18150
  `
18125
18151
  );
18126
- const runId = extractRunId(input2.status);
18152
+ const runId = extractRunId(input2.status) ?? firstCollectedStringField(input2.status, "runId");
18127
18153
  const error = firstCollectedStringField(input2.status, "error") ?? firstCollectedStringField(input2.status, "message");
18128
18154
  if (runId) {
18129
18155
  process.stderr.write(`Run id: ${runId}
@@ -18339,6 +18365,38 @@ function collectHardFailureAliasSpecs(config) {
18339
18365
  }
18340
18366
  return aliases;
18341
18367
  }
18368
+ function collectWaterfallAliasSpecs(config) {
18369
+ const aliases = [];
18370
+ const seen = /* @__PURE__ */ new Set();
18371
+ for (const command of config.commands) {
18372
+ if (!("with_waterfall" in command)) {
18373
+ continue;
18374
+ }
18375
+ const alias = command.with_waterfall.trim();
18376
+ if (!alias || seen.has(alias)) {
18377
+ continue;
18378
+ }
18379
+ const activeChildren = command.commands.filter(
18380
+ (child) => !("with_waterfall" in child) && !child.disabled
18381
+ );
18382
+ if (activeChildren.length === 0) {
18383
+ continue;
18384
+ }
18385
+ seen.add(alias);
18386
+ const childOperations = activeChildren.map((child) => child.operation?.trim() || child.tool.trim()).filter(Boolean);
18387
+ aliases.push({
18388
+ alias,
18389
+ childAliases: activeChildren.map((child) => child.alias).filter(Boolean),
18390
+ childToolsByAlias: new Map(
18391
+ activeChildren.map(
18392
+ (child) => [child.alias, child.tool.trim()]
18393
+ )
18394
+ ),
18395
+ operation: childOperations.length > 0 ? childOperations.join(", ") : void 0
18396
+ });
18397
+ }
18398
+ return aliases;
18399
+ }
18342
18400
  function hardFailureOperationByAlias(config) {
18343
18401
  const operations = /* @__PURE__ */ new Map();
18344
18402
  if (!config) {
@@ -18351,6 +18409,60 @@ function hardFailureOperationByAlias(config) {
18351
18409
  }
18352
18410
  return operations;
18353
18411
  }
18412
+ function isWaterfallResultMeaningful(value) {
18413
+ if (Array.isArray(value)) {
18414
+ return value.some(isWaterfallResultMeaningful);
18415
+ }
18416
+ if (value && typeof value === "object") {
18417
+ const record = value;
18418
+ const status = typeof record.status === "string" ? record.status.trim().toLowerCase() : "";
18419
+ if (status === "error" || status === "failed") {
18420
+ return false;
18421
+ }
18422
+ if (typeof record.error === "string" && record.error.trim()) {
18423
+ return false;
18424
+ }
18425
+ if (Object.prototype.hasOwnProperty.call(record, "matched_result")) {
18426
+ return isWaterfallResultMeaningful(record.matched_result);
18427
+ }
18428
+ for (const key of [
18429
+ "value",
18430
+ "email",
18431
+ "phone",
18432
+ "phone_number",
18433
+ "mobile_phone",
18434
+ "mobile_phone_number",
18435
+ "url",
18436
+ "domain",
18437
+ "name",
18438
+ "result",
18439
+ "data"
18440
+ ]) {
18441
+ if (isWaterfallResultMeaningful(record[key])) {
18442
+ return true;
18443
+ }
18444
+ }
18445
+ return Object.entries(record).some(
18446
+ ([key, entry]) => !ENRICH_FLATTENED_CONTROL_FIELDS.has(key) && isWaterfallResultMeaningful(entry)
18447
+ );
18448
+ }
18449
+ return isNonEmptyCsvCell(value);
18450
+ }
18451
+ function aliasHasMeaningfulResult(row, alias) {
18452
+ return aliasFailureCellCandidates(row, alias).some(
18453
+ isWaterfallResultMeaningful
18454
+ );
18455
+ }
18456
+ function stableRowSnapshot(value) {
18457
+ if (Array.isArray(value)) {
18458
+ return `[${value.map(stableRowSnapshot).join(",")}]`;
18459
+ }
18460
+ if (value && typeof value === "object") {
18461
+ const record = value;
18462
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableRowSnapshot(record[key])}`).join(",")}}`;
18463
+ }
18464
+ return JSON.stringify(value);
18465
+ }
18354
18466
  function cellFailureError(value) {
18355
18467
  const parsed = parseMaybeJsonObject(value);
18356
18468
  if (!isRecord7(parsed)) {
@@ -18496,6 +18608,112 @@ function collectEnrichFailureJobs(input2) {
18496
18608
  });
18497
18609
  return jobs;
18498
18610
  }
18611
+ function collectEmptyWaterfallIssues(input2) {
18612
+ const aliases = collectWaterfallAliasSpecs(input2.config);
18613
+ if (aliases.length === 0 || input2.rows.length === 0 || !input2.rowSetComplete) {
18614
+ return [];
18615
+ }
18616
+ const runId = extractRunId(input2.status) ?? firstCollectedStringField(input2.status, "runId");
18617
+ const followUpCommands = collectEnrichFollowUpCommands({
18618
+ status: input2.status,
18619
+ runId,
18620
+ outputPath: input2.followUpOutputPath ?? input2.outputPath
18621
+ });
18622
+ const issues = [];
18623
+ aliases.forEach((spec) => {
18624
+ if (waterfallExecutionSignal(input2.status, spec) === "all_condition_skipped") {
18625
+ return;
18626
+ }
18627
+ const logicalRows = /* @__PURE__ */ new Map();
18628
+ input2.rows.forEach((row, rowIndex) => {
18629
+ const sourceRowId = sourceRowIndexFromEnrichRow(
18630
+ row,
18631
+ 0,
18632
+ Number.MAX_SAFE_INTEGER
18633
+ );
18634
+ const rowKey = sourceRowId !== null ? `source:${sourceRowId}` : `snapshot:${stableRowSnapshot(row)}`;
18635
+ const existing = logicalRows.get(rowKey);
18636
+ const hasMeaningfulResult = aliasHasMeaningfulResult(row, spec.alias);
18637
+ logicalRows.set(rowKey, {
18638
+ hasMeaningfulResult: (existing?.hasMeaningfulResult ?? false) || hasMeaningfulResult
18639
+ });
18640
+ });
18641
+ const emptyRows = Array.from(logicalRows.values()).filter(
18642
+ (row) => !row.hasMeaningfulResult
18643
+ );
18644
+ if (emptyRows.length !== logicalRows.size) {
18645
+ return;
18646
+ }
18647
+ const selectedRows = logicalRows.size;
18648
+ const message = `Waterfall ${spec.alias} produced 0 values for ${selectedRows} selected row(s).`;
18649
+ issues.push({
18650
+ issue_id: `${spec.alias}-empty-waterfall`,
18651
+ kind: "empty_waterfall",
18652
+ severity: "needs_attention",
18653
+ column: spec.alias,
18654
+ selected_rows: selectedRows,
18655
+ rows_with_value: 0,
18656
+ rows_without_value: selectedRows,
18657
+ message,
18658
+ next_steps: enrichIssueNextSteps({ runId }),
18659
+ follow_up_commands: followUpCommands,
18660
+ ...spec.operation ? { operation: spec.operation } : {},
18661
+ ...runId ? { run_id: runId } : {}
18662
+ });
18663
+ });
18664
+ return issues;
18665
+ }
18666
+ function waterfallExecutionSignal(status, spec) {
18667
+ const childAliases = spec.childAliases ?? [];
18668
+ if (childAliases.length === 0) {
18669
+ return "unknown";
18670
+ }
18671
+ const summaries = collectColumnStats(status);
18672
+ let sawChildStats = false;
18673
+ let everyChildStatOnlyConditionSkipped = true;
18674
+ const childAliasesWithStats = /* @__PURE__ */ new Set();
18675
+ for (const childAlias of childAliases) {
18676
+ for (const stat2 of summaries.map((summary) => summary[childAlias]).filter(isRecord7)) {
18677
+ sawChildStats = true;
18678
+ childAliasesWithStats.add(childAlias);
18679
+ const execution = isRecord7(stat2.execution) ? stat2.execution : null;
18680
+ const executedSummary = parseExecutionSummary(
18681
+ execution?.["completed:executed"]
18682
+ );
18683
+ const skippedConditionSummary = parseExecutionSummary(
18684
+ execution?.["skipped:condition"]
18685
+ );
18686
+ const executedCount = executedSummary?.count ?? 0;
18687
+ const skippedConditionCount = skippedConditionSummary?.count ?? 0;
18688
+ const totalCount = executedSummary?.total ?? skippedConditionSummary?.total;
18689
+ const onlyConditionSkips = totalCount !== void 0 && skippedConditionCount >= totalCount && executedCount <= skippedConditionCount;
18690
+ if (executedCount > 0 && !onlyConditionSkips || numericFailedRowSignal(execution?.failed) > 0 || numericFailedRowSignal(stat2.failed) > 0 || numericFailedRowSignal(stat2.failedRows) > 0) {
18691
+ return "executed";
18692
+ }
18693
+ if (!onlyConditionSkips) {
18694
+ everyChildStatOnlyConditionSkipped = false;
18695
+ }
18696
+ }
18697
+ }
18698
+ if (sawChildStats && childAliasesWithStats.size === childAliases.length && everyChildStatOnlyConditionSkipped) {
18699
+ return "all_condition_skipped";
18700
+ }
18701
+ return "unknown";
18702
+ }
18703
+ function mergeEnrichFailureJobs(...jobGroups) {
18704
+ const merged = [];
18705
+ const seen = /* @__PURE__ */ new Set();
18706
+ for (const jobs of jobGroups) {
18707
+ for (const job of jobs) {
18708
+ if (seen.has(job.job_id)) {
18709
+ continue;
18710
+ }
18711
+ seen.add(job.job_id);
18712
+ merged.push(job);
18713
+ }
18714
+ }
18715
+ return merged;
18716
+ }
18499
18717
  function collectStatusFailureJobs(input2) {
18500
18718
  const aliases = collectHardFailureAliasSpecs(input2.config);
18501
18719
  if (aliases.length === 0) {
@@ -18589,14 +18807,23 @@ function collectCompleteStatusFailureMessages(input2) {
18589
18807
  return complete;
18590
18808
  }
18591
18809
  function parseExecutionCount(value) {
18810
+ return parseExecutionSummary(value)?.count ?? null;
18811
+ }
18812
+ function parseExecutionSummary(value) {
18592
18813
  if (typeof value === "number" && Number.isFinite(value)) {
18593
- return Math.max(0, Math.trunc(value));
18814
+ return { count: Math.max(0, Math.trunc(value)) };
18594
18815
  }
18595
18816
  if (typeof value !== "string") {
18596
18817
  return null;
18597
18818
  }
18598
- const match = value.trim().match(/^(\d+)\s*\//);
18599
- return match?.[1] ? Number.parseInt(match[1], 10) : null;
18819
+ const match = value.trim().match(/^(\d+)(?:\s*\/\s*(\d+))?/);
18820
+ if (!match?.[1]) {
18821
+ return null;
18822
+ }
18823
+ return {
18824
+ count: Number.parseInt(match[1], 10),
18825
+ ...match[2] ? { total: Number.parseInt(match[2], 10) } : {}
18826
+ };
18600
18827
  }
18601
18828
  function executionSummaryText(count, total) {
18602
18829
  const percent = total > 0 ? Math.round(count / total * 100) : 0;
@@ -18722,19 +18949,145 @@ function summarizeFailedJobError(value) {
18722
18949
  const text = String(value ?? "").trim();
18723
18950
  return text.length > 500 ? `${text.slice(0, 497)}...` : text;
18724
18951
  }
18952
+ function shellSingleQuote2(value) {
18953
+ return `'${value.replace(/'/g, `'\\''`)}'`;
18954
+ }
18955
+ function addEnrichFollowUpCommand(commands, seen, command) {
18956
+ const normalized = command.command.trim();
18957
+ if (!normalized || seen.has(normalized)) {
18958
+ return;
18959
+ }
18960
+ seen.add(normalized);
18961
+ commands.push({ ...command, command: normalized });
18962
+ }
18963
+ function datasetSelectorForEnrichCommand(record, fallbackPath) {
18964
+ const candidates = [record.path, record.tableNamespace, fallbackPath];
18965
+ for (const candidate of candidates) {
18966
+ if (typeof candidate === "string" && candidate.trim()) {
18967
+ return candidate.trim();
18968
+ }
18969
+ }
18970
+ return null;
18971
+ }
18972
+ function collectEnrichFollowUpCommands(input2) {
18973
+ const commands = [];
18974
+ const seen = /* @__PURE__ */ new Set();
18975
+ if (input2.runId) {
18976
+ addEnrichFollowUpCommand(commands, seen, {
18977
+ label: "inspect run",
18978
+ command: `deepline runs get ${input2.runId} --full --json`
18979
+ });
18980
+ }
18981
+ collectDatasetFollowUpCommands(input2.status, {
18982
+ runId: input2.runId,
18983
+ outputPath: input2.outputPath,
18984
+ commands,
18985
+ seen,
18986
+ path: "result",
18987
+ depth: 0
18988
+ });
18989
+ if (input2.runId && input2.outputPath && !commands.some((command) => command.label === "export enrich rows")) {
18990
+ addEnrichFollowUpCommand(commands, seen, {
18991
+ label: "export enrich rows",
18992
+ path: GENERATED_ENRICH_ROWS_TABLE_NAMESPACE,
18993
+ command: `deepline runs export ${input2.runId} --dataset ${GENERATED_ENRICH_ROWS_TABLE_NAMESPACE} --out ${shellSingleQuote2(
18994
+ (0, import_node_path12.resolve)(input2.outputPath)
18995
+ )}`
18996
+ });
18997
+ }
18998
+ return commands.slice(0, 8);
18999
+ }
19000
+ function sidecarEnrichRowsExportPath(outputPath) {
19001
+ const resolved = (0, import_node_path12.resolve)(outputPath);
19002
+ const ext = (0, import_node_path12.extname)(resolved) || ".csv";
19003
+ const stem = (0, import_node_path12.basename)(resolved, ext);
19004
+ return (0, import_node_path12.join)((0, import_node_path12.dirname)(resolved), `${stem}.deepline-enrich-rows${ext}`);
19005
+ }
19006
+ function collectDatasetFollowUpCommands(value, state) {
19007
+ if (state.depth > 12 || !value || typeof value !== "object" || state.commands.length >= 8) {
19008
+ return;
19009
+ }
19010
+ if (Array.isArray(value)) {
19011
+ value.slice(0, 50).forEach(
19012
+ (entry, index) => collectDatasetFollowUpCommands(entry, {
19013
+ ...state,
19014
+ path: `${state.path}.${index}`,
19015
+ depth: state.depth + 1
19016
+ })
19017
+ );
19018
+ return;
19019
+ }
19020
+ const record = value;
19021
+ const isDataset = record.kind === "dataset";
19022
+ if (isDataset) {
19023
+ const selector = datasetSelectorForEnrichCommand(record, state.path);
19024
+ const labelPath = typeof record.path === "string" && record.path.trim() ? record.path.trim() : selector ?? state.path;
19025
+ if (typeof record.queryDatasetCommand === "string") {
19026
+ addEnrichFollowUpCommand(state.commands, state.seen, {
19027
+ label: `query ${labelPath}`,
19028
+ path: labelPath,
19029
+ command: record.queryDatasetCommand
19030
+ });
19031
+ }
19032
+ const exportCommand = typeof record.slowExportAsCsvCommand === "string" ? record.slowExportAsCsvCommand : typeof record.fullExportCommand === "string" ? record.fullExportCommand : null;
19033
+ if (exportCommand) {
19034
+ addEnrichFollowUpCommand(state.commands, state.seen, {
19035
+ label: `export ${labelPath}`,
19036
+ path: labelPath,
19037
+ command: exportCommand
19038
+ });
19039
+ } else if (state.runId && state.outputPath && selector) {
19040
+ addEnrichFollowUpCommand(state.commands, state.seen, {
19041
+ label: selector === GENERATED_ENRICH_ROWS_TABLE_NAMESPACE ? "export enrich rows" : `export ${labelPath}`,
19042
+ path: selector,
19043
+ command: `deepline runs export ${state.runId} --dataset ${shellSingleQuote2(
19044
+ selector
19045
+ )} --out ${shellSingleQuote2((0, import_node_path12.resolve)(state.outputPath))}`
19046
+ });
19047
+ }
19048
+ }
19049
+ for (const [key, child] of Object.entries(record)) {
19050
+ if (key === "preview" || key === "access") {
19051
+ continue;
19052
+ }
19053
+ collectDatasetFollowUpCommands(child, {
19054
+ ...state,
19055
+ path: `${state.path}.${key}`,
19056
+ depth: state.depth + 1
19057
+ });
19058
+ if (state.commands.length >= 8) {
19059
+ return;
19060
+ }
19061
+ }
19062
+ }
19063
+ function enrichIssueNextSteps(input2) {
19064
+ const steps = [
19065
+ "Inspect the run output and returned dataset handles.",
19066
+ "If runs get returns queryDatasetCommand or slowExportAsCsvCommand, run that command to fetch durable rows from the customer DB.",
19067
+ "For provider-backed waterfalls, execute the child provider tool on a sample row to confirm the provider returns the expected field."
19068
+ ];
19069
+ if (!input2.runId) {
19070
+ return steps;
19071
+ }
19072
+ return [
19073
+ `Inspect run output: deepline runs get ${input2.runId} --full --json`,
19074
+ ...steps.slice(1)
19075
+ ];
19076
+ }
18725
19077
  function formatFailureReportCommand(reportPath) {
18726
19078
  const quoted = reportPath.replace(/'/g, `'\\''`);
18727
19079
  return `Inspect failed jobs: jq -r '.jobs[] | [.job_id,.row_id,.col_index,.column,.status,.last_error] | @tsv' '${quoted}'`;
18728
19080
  }
18729
19081
  async function persistEnrichFailureReport(input2) {
18730
- if (input2.jobs.length === 0) {
19082
+ if (input2.jobs.length === 0 && input2.issues.length === 0) {
18731
19083
  return null;
18732
19084
  }
18733
19085
  const stateDir = (0, import_node_path12.join)((0, import_node_os8.homedir)(), ".local", "deepline", "runtime", "state");
19086
+ const reportPrefix = input2.jobs.length > 0 ? "run-block-failures" : "enrich-issues";
18734
19087
  await (0, import_promises3.mkdir)(stateDir, { recursive: true });
18735
19088
  const reportPath = (0, import_node_path12.join)(
18736
19089
  stateDir,
18737
- `run-block-failures-${Math.floor(Date.now() / 1e3)}-${process.pid}.json`
19090
+ `${reportPrefix}-${Math.floor(Date.now() / 1e3)}-${process.pid}.json`
18738
19091
  );
18739
19092
  const report = {
18740
19093
  generated_at: (/* @__PURE__ */ new Date()).toISOString(),
@@ -18749,6 +19102,7 @@ async function persistEnrichFailureReport(input2) {
18749
19102
  },
18750
19103
  jobs: input2.jobs,
18751
19104
  failed_jobs: input2.jobs,
19105
+ enrich_issues: input2.issues,
18752
19106
  canceled_jobs: [],
18753
19107
  pending_jobs: [],
18754
19108
  missing_job_ids: []
@@ -18761,48 +19115,100 @@ async function persistEnrichFailureReport(input2) {
18761
19115
  return reportPath;
18762
19116
  }
18763
19117
  async function maybeEmitEnrichFailureReport(input2) {
19118
+ const fallbackRowsInfo = extractCanonicalRowsInfo(input2.status);
19119
+ const rowSetComplete = input2.rowSetComplete ?? Boolean(
19120
+ fallbackRowsInfo?.complete && input2.rows.length >= fallbackRowsInfo.totalRows
19121
+ );
18764
19122
  const rowJobs = collectEnrichFailureJobs({
18765
19123
  config: input2.config,
18766
19124
  rows: input2.rows,
18767
19125
  rowStart: input2.rowRange.rowStart
18768
19126
  });
18769
- const jobs = rowJobs.length > 0 || input2.status === void 0 ? rowJobs : collectStatusFailureJobs({
19127
+ const enrichIssues = collectEmptyWaterfallIssues({
19128
+ config: input2.config,
19129
+ rows: input2.rows,
19130
+ status: input2.waterfallStatus ?? input2.status,
19131
+ outputPath: input2.outputPath,
19132
+ followUpOutputPath: input2.followUpOutputPath,
19133
+ rowSetComplete
19134
+ });
19135
+ const statusJobs = input2.status === void 0 || rowJobs.length > 0 ? [] : collectStatusFailureJobs({
18770
19136
  config: input2.config,
18771
19137
  status: input2.status,
18772
19138
  rowRange: input2.statusRowRange ?? input2.rowRange
18773
19139
  });
18774
- if (jobs.length === 0) {
19140
+ const jobs = rowJobs.length > 0 || statusJobs.length > 0 || input2.status === void 0 ? mergeEnrichFailureJobs(rowJobs, statusJobs) : [];
19141
+ if (jobs.length === 0 && enrichIssues.length === 0) {
18775
19142
  return null;
18776
19143
  }
18777
19144
  const reportPath = await persistEnrichFailureReport({
18778
19145
  jobs,
19146
+ issues: enrichIssues,
18779
19147
  apiUrl: input2.client.baseUrl,
18780
19148
  outputPath: input2.outputPath,
18781
19149
  rows: input2.rowRange
18782
19150
  });
18783
- process.stderr.write("Execution failed.\n");
18784
- process.stderr.write("Failure preview (up to 5 failed jobs):\n");
18785
- process.stderr.write("job_id row_id col_index column status error\n");
18786
- for (const job of jobs.slice(0, 5)) {
18787
- process.stderr.write(
18788
- `${job.job_id} ${job.row_id} ${job.col_index} ${job.column} ${job.status} ${summarizeFailedJobError(job.last_error)}
19151
+ if (jobs.length > 0) {
19152
+ process.stderr.write("Execution failed.\n");
19153
+ process.stderr.write("Failure preview (up to 5 failed jobs):\n");
19154
+ process.stderr.write("job_id row_id col_index column status error\n");
19155
+ for (const job of jobs.slice(0, 5)) {
19156
+ process.stderr.write(
19157
+ `${job.job_id} ${job.row_id} ${job.col_index} ${job.column} ${job.status} ${summarizeFailedJobError(job.last_error)}
18789
19158
  `
18790
- );
19159
+ );
19160
+ }
19161
+ if (jobs.length > 5) {
19162
+ process.stderr.write(`Showing 5 of ${jobs.length} failed jobs.
19163
+ `);
19164
+ }
18791
19165
  }
18792
- if (jobs.length > 5) {
18793
- process.stderr.write(`Showing 5 of ${jobs.length} failed jobs.
19166
+ if (enrichIssues.length > 0) {
19167
+ process.stderr.write("Enrichment needs attention.\n");
19168
+ process.stderr.write("Issue preview (up to 5 enrichment issues):\n");
19169
+ process.stderr.write(
19170
+ "issue_id column severity selected_rows rows_with_value message\n"
19171
+ );
19172
+ for (const issue of enrichIssues.slice(0, 5)) {
19173
+ process.stderr.write(
19174
+ `${issue.issue_id} ${issue.column} ${issue.severity} ${issue.selected_rows} ${issue.rows_with_value} ${issue.message}
19175
+ `
19176
+ );
19177
+ for (const step of issue.next_steps.slice(0, 2)) {
19178
+ process.stderr.write(`Next: ${step}
19179
+ `);
19180
+ }
19181
+ for (const command of issue.follow_up_commands.slice(0, 4)) {
19182
+ process.stderr.write(`Command: ${command.label}: ${command.command}
18794
19183
  `);
19184
+ }
19185
+ }
19186
+ if (enrichIssues.length > 5) {
19187
+ process.stderr.write(
19188
+ `Showing 5 of ${enrichIssues.length} enrichment issues.
19189
+ `
19190
+ );
19191
+ }
18795
19192
  }
18796
19193
  if (reportPath) {
18797
- process.stderr.write(`Full failure report: ${reportPath}
19194
+ if (jobs.length > 0) {
19195
+ process.stderr.write(`Full failure report: ${reportPath}
18798
19196
  `);
18799
- process.stderr.write(`${formatFailureReportCommand(reportPath)}
19197
+ process.stderr.write(`${formatFailureReportCommand(reportPath)}
18800
19198
  `);
18801
- process.stderr.write("Enrichment failed. See failure report above.\n");
18802
- return { path: reportPath, jobs };
19199
+ } else {
19200
+ process.stderr.write(`Full enrich report: ${reportPath}
19201
+ `);
19202
+ }
19203
+ process.stderr.write(
19204
+ jobs.length > 0 ? "Enrichment failed. See report above.\n" : "Enrichment produced no values for at least one requested waterfall. See issue preview above.\n"
19205
+ );
19206
+ return { path: reportPath, jobs, issues: enrichIssues };
18803
19207
  }
18804
- process.stderr.write("Enrichment failed.\n");
18805
- return { path: "", jobs };
19208
+ process.stderr.write(
19209
+ jobs.length > 0 ? "Enrichment failed.\n" : "Enrichment needs attention.\n"
19210
+ );
19211
+ return { path: "", jobs, issues: enrichIssues };
18806
19212
  }
18807
19213
  function mergePreferredColumns(preferredColumns, rows) {
18808
19214
  const columns = [];
@@ -19468,6 +19874,7 @@ function registerEnrichCommand(program) {
19468
19874
  const inPlaceFinalOutputPath = options.inPlace ? (0, import_node_path12.resolve)(inputCsv) : null;
19469
19875
  const inPlaceCommitOutputPath = options.inPlace ? (await (0, import_promises3.lstat)(inputCsv)).isSymbolicLink() ? await (0, import_promises3.realpath)(inputCsv) : inPlaceFinalOutputPath : null;
19470
19876
  const failureReportOutputPath = options.inPlace ? inPlaceFinalOutputPath : null;
19877
+ const enrichIssueFollowUpOutputPath = options.inPlace && inPlaceFinalOutputPath ? sidecarEnrichRowsExportPath(inPlaceFinalOutputPath) : null;
19471
19878
  let inPlaceCommitted = false;
19472
19879
  const commitInPlaceOutput = async (exportResult) => {
19473
19880
  if (!inPlaceTempOutputPath || !inPlaceCommitOutputPath || !inPlaceFinalOutputPath) {
@@ -19556,6 +19963,7 @@ function registerEnrichCommand(program) {
19556
19963
  }
19557
19964
  let workingMergeSourceCsvPath = sourceCsvPath;
19558
19965
  let lastStatus = null;
19966
+ const batchStatuses = [];
19559
19967
  let finalExportResult = null;
19560
19968
  let totalEnrichedRows = 0;
19561
19969
  const batchFailureRows = [];
@@ -19593,6 +20001,7 @@ function registerEnrichCommand(program) {
19593
20001
  suppressOpen: true
19594
20002
  });
19595
20003
  lastStatus = chunk.status;
20004
+ batchStatuses.push(chunk.status);
19596
20005
  if (chunk.captured.result !== 0) {
19597
20006
  if (chunk.exportResult) {
19598
20007
  finalExportResult = chunk.exportResult;
@@ -19612,7 +20021,10 @@ function registerEnrichCommand(program) {
19612
20021
  statusRowRange: chunkRows,
19613
20022
  client: client2,
19614
20023
  outputPath: failureReportOutputPath ?? finalExportResult?.path ?? outputPath,
19615
- status: chunk.status
20024
+ followUpOutputPath: enrichIssueFollowUpOutputPath,
20025
+ status: chunk.status,
20026
+ waterfallStatus: batchStatuses,
20027
+ ...finalExportResult ? { rowSetComplete: !finalExportResult.partial } : {}
19616
20028
  });
19617
20029
  const committedExportResult2 = finalExportResult ? await commitInPlaceOutput(finalExportResult) : null;
19618
20030
  if (options.json) {
@@ -19634,12 +20046,7 @@ function registerEnrichCommand(program) {
19634
20046
  partial: true
19635
20047
  } : {}
19636
20048
  } : null,
19637
- ...failureReport3 ? {
19638
- failure_report: {
19639
- path: failureReport3.path,
19640
- jobs: failureReport3.jobs.length
19641
- }
19642
- } : {}
20049
+ ...enrichReportJson(failureReport3)
19643
20050
  });
19644
20051
  } else {
19645
20052
  if (committedExportResult2) {
@@ -19678,7 +20085,10 @@ function registerEnrichCommand(program) {
19678
20085
  },
19679
20086
  client: client2,
19680
20087
  outputPath: failureReportOutputPath ?? finalExportResult?.path ?? outputPath,
19681
- status: lastStatus
20088
+ followUpOutputPath: enrichIssueFollowUpOutputPath,
20089
+ status: lastStatus,
20090
+ waterfallStatus: batchStatuses,
20091
+ ...finalExportResult ? { rowSetComplete: !finalExportResult.partial } : {}
19682
20092
  });
19683
20093
  finalExportResult = await commitInPlaceOutput(finalExportResult);
19684
20094
  if (options.json) {
@@ -19710,12 +20120,7 @@ function registerEnrichCommand(program) {
19710
20120
  enrichedRows: totalEnrichedRows,
19711
20121
  path: finalExportResult.path
19712
20122
  } : null,
19713
- ...failureReport2 ? {
19714
- failure_report: {
19715
- path: failureReport2.path,
19716
- jobs: failureReport2.jobs.length
19717
- }
19718
- } : {}
20123
+ ...enrichReportJson(failureReport2)
19719
20124
  });
19720
20125
  }
19721
20126
  if (failureReport2) {
@@ -19746,6 +20151,7 @@ function registerEnrichCommand(program) {
19746
20151
  exportResult: committedExportResult2,
19747
20152
  outputPath,
19748
20153
  reportOutputPath: failureReportOutputPath,
20154
+ followUpOutputPath: enrichIssueFollowUpOutputPath,
19749
20155
  status
19750
20156
  });
19751
20157
  if (options.json) {
@@ -19753,12 +20159,7 @@ function registerEnrichCommand(program) {
19753
20159
  ok: false,
19754
20160
  run: status,
19755
20161
  output: enrichOutputJson(committedExportResult2),
19756
- ...failureReport2 ? {
19757
- failure_report: {
19758
- path: failureReport2.path,
19759
- jobs: failureReport2.jobs.length
19760
- }
19761
- } : {}
20162
+ ...enrichReportJson(failureReport2)
19762
20163
  });
19763
20164
  }
19764
20165
  process.exitCode = failureReport2 ? EXIT_SERVER2 : captured.result;
@@ -19773,6 +20174,7 @@ function registerEnrichCommand(program) {
19773
20174
  exportResult,
19774
20175
  outputPath,
19775
20176
  reportOutputPath: failureReportOutputPath,
20177
+ followUpOutputPath: enrichIssueFollowUpOutputPath,
19776
20178
  status
19777
20179
  });
19778
20180
  const committedExportResult2 = await commitInPlaceOutput(exportResult);
@@ -19787,12 +20189,7 @@ function registerEnrichCommand(program) {
19787
20189
  ok: !failureReport2,
19788
20190
  run,
19789
20191
  output: enrichOutputJson(committedExportResult2),
19790
- ...failureReport2 ? {
19791
- failure_report: {
19792
- path: failureReport2.path,
19793
- jobs: failureReport2.jobs.length
19794
- }
19795
- } : {}
20192
+ ...enrichReportJson(failureReport2)
19796
20193
  });
19797
20194
  if (failureReport2) {
19798
20195
  process.exitCode = EXIT_SERVER2;
@@ -19805,7 +20202,9 @@ function registerEnrichCommand(program) {
19805
20202
  rowRange: rows,
19806
20203
  client: client2,
19807
20204
  outputPath: failureReportOutputPath ?? exportResult?.path ?? null,
19808
- status
20205
+ followUpOutputPath: enrichIssueFollowUpOutputPath,
20206
+ status,
20207
+ ...exportResult ? { rowSetComplete: !exportResult.partial } : {}
19809
20208
  });
19810
20209
  const committedExportResult = await commitInPlaceOutput(exportResult);
19811
20210
  if (committedExportResult) {