deepline 0.3.187 → 0.3.188

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.
@@ -200,7 +200,7 @@ export const SDK_RELEASE = {
200
200
  // getters keep their established compatibility behavior.
201
201
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
202
202
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
203
- version: '0.3.187',
203
+ version: '0.3.188',
204
204
  updateSummary:
205
205
  'Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.',
206
206
  packageCapabilities: {
@@ -88,6 +88,7 @@ import {
88
88
  activeRuntimeSheetAttemptFenceSql,
89
89
  newerTerminalRuntimeSheetRowSql,
90
90
  releasableRuntimeSheetAttemptFenceSql,
91
+ sameOwnerTerminalAttemptEpochSql,
91
92
  } from './sheet-attempt-sql';
92
93
  import type { MapRowOutcome } from './durability-store';
93
94
  import { RUNTIME_CAPACITY_POLICY } from './runtime-capacity-policy';
@@ -7119,6 +7120,7 @@ export type RuntimeMapRowsWriteResult = {
7119
7120
  type RuntimeMapRowsMutationResult = {
7120
7121
  updated: number;
7121
7122
  fencedRows: RuntimeMapRowsStaleRow[];
7123
+ conflictKeys: string[];
7122
7124
  };
7123
7125
 
7124
7126
  const RUNTIME_MAP_ROWS_STALE_REASONS: ReadonlySet<RuntimeMapRowsStaleReason> =
@@ -7308,6 +7310,7 @@ async function completeRuntimeMapRowChunks(
7308
7310
  ): Promise<RuntimeMapRowsMutationResult> {
7309
7311
  let updated = 0;
7310
7312
  const fencedRows: RuntimeMapRowsStaleRow[] = [];
7313
+ const conflictKeys: string[] = [];
7311
7314
  for (const chunk of input.chunks) {
7312
7315
  const chunkKeys = chunk.map((row) => row.key);
7313
7316
  const chunkInputIndexes = chunk.map((row) => row.input_index);
@@ -7335,6 +7338,7 @@ async function completeRuntimeMapRowChunks(
7335
7338
  updated: number;
7336
7339
  matched_keys: string[];
7337
7340
  fenced_rows: unknown;
7341
+ conflict_keys: string[];
7338
7342
  }>(
7339
7343
  `WITH updates AS (
7340
7344
  SELECT key_values._key,
@@ -7351,7 +7355,6 @@ async function completeRuntimeMapRowChunks(
7351
7355
  ),
7352
7356
  matched_updates AS (
7353
7357
  SELECT target._key AS matched_key,
7354
- updates._key AS submitted_key,
7355
7358
  target._status AS prev_status,
7356
7359
  target._cell_meta AS prev_cell_meta,
7357
7360
  updates.input_index,
@@ -7361,6 +7364,40 @@ async function completeRuntimeMapRowChunks(
7361
7364
  JOIN ${sheetTable(session)} AS target
7362
7365
  ON target._key = updates._key
7363
7366
  ),
7367
+ same_version_conflicts AS (
7368
+ SELECT updates.matched_key AS _key
7369
+ FROM matched_updates AS updates
7370
+ JOIN ${sheetTable(session)} AS target
7371
+ ON target._key = updates.matched_key
7372
+ WHERE $13::bigint IS NOT NULL
7373
+ AND target._write_version = $13::bigint
7374
+ AND target._run_id = $5::text
7375
+ AND target._status IN ('enriched', 'failed')
7376
+ AND (
7377
+ EXISTS (
7378
+ SELECT 1
7379
+ FROM unnest($8::text[]) AS field_values(field)
7380
+ WHERE target._cell_meta -> field_values.field ->> 'runId' = $5::text
7381
+ )
7382
+ OR (
7383
+ coalesce(target._attempt_owner_run_id, target._run_id) = $10::text
7384
+ AND COALESCE(target._attempt_seq, 0) = $12::integer
7385
+ )
7386
+ )
7387
+ AND (
7388
+ target._status <> 'enriched'
7389
+ OR (${targetChangedPatchedCellSql})
7390
+ )
7391
+ -- First terminal wins: suppress conflict for same owner+epoch already-terminal row.
7392
+ -- Durable replay (omitted_terminal_replay) from same attempt epoch is idempotent.
7393
+ AND NOT (
7394
+ $10::text IS NOT NULL
7395
+ AND $12::integer IS NOT NULL
7396
+ AND target._status = 'enriched'
7397
+ AND coalesce(target._attempt_owner_run_id, target._run_id) = $10::text
7398
+ AND COALESCE(target._attempt_seq, 0) = $12::integer
7399
+ )
7400
+ ),
7364
7401
  applied_rows AS (
7365
7402
  UPDATE ${sheetTable(session)} AS target
7366
7403
  SET _status = 'enriched',
@@ -7377,6 +7414,7 @@ async function completeRuntimeMapRowChunks(
7377
7414
  _cell_meta = ${mergeRuntimeCellMetaPatchSql('target._cell_meta', 'updates.cell_meta_patch')}${input.physicalUpdateSetSql}
7378
7415
  FROM matched_updates AS updates
7379
7416
  WHERE target._key = updates.matched_key
7417
+ AND NOT EXISTS (SELECT 1 FROM same_version_conflicts)
7380
7418
  AND ($13::bigint IS NULL OR target._write_version = $13::bigint)
7381
7419
  AND ($13::bigint IS NOT NULL OR target._status <> 'enriched')
7382
7420
  AND NOT (${newerTerminalRuntimeSheetRowSql('target', '$11::timestamptz', '$12::integer', '$9::text', '$10::text')})
@@ -7386,27 +7424,14 @@ async function completeRuntimeMapRowChunks(
7386
7424
  OR ($9::text IS NOT NULL AND ${activeRuntimeSheetAttemptFenceSql('target', '$9::text', '$10::text', '$11::timestamptz', '$12::integer')})
7387
7425
  OR ($9::text IS NOT NULL AND ${writableEnrichedAttemptSql})
7388
7426
  )
7389
- -- A same-version enriched row is already the durable result for this
7390
- -- logical run. Provider retries may produce a different payload or
7391
- -- timestamp; preserve the first terminal value. Input-index repair
7392
- -- remains writable when the submitted key itself changed.
7427
+ -- First terminal wins: skip UPDATE for already-terminal same-owner+epoch row.
7428
+ -- Even though we excluded from conflicts, we still no-op here for idempotency.
7393
7429
  AND NOT (
7394
- $13::bigint IS NOT NULL
7395
- AND target._write_version = $13::bigint
7396
- AND target._run_id = $5::text
7430
+ $10::text IS NOT NULL
7431
+ AND $12::integer IS NOT NULL
7397
7432
  AND target._status = 'enriched'
7398
- AND updates.submitted_key = target._key
7399
- AND (
7400
- EXISTS (
7401
- SELECT 1
7402
- FROM unnest($8::text[]) AS field_values(field)
7403
- WHERE target._cell_meta -> field_values.field ->> 'runId' = $5::text
7404
- )
7405
- OR (
7406
- $10::text IS NOT NULL
7407
- AND target._attempt_owner_run_id IS NOT DISTINCT FROM $10::text
7408
- )
7409
- )
7433
+ AND coalesce(target._attempt_owner_run_id, target._run_id) = $10::text
7434
+ AND COALESCE(target._attempt_seq, 0) = $12::integer
7410
7435
  )
7411
7436
  AND (
7412
7437
  target._status <> 'enriched'
@@ -7539,6 +7564,7 @@ async function completeRuntimeMapRowChunks(
7539
7564
  (SELECT count(*)::int FROM applied_rows) AS updated,
7540
7565
  coalesce((SELECT array_agg(matched_key) FROM matched_updates), '{}'::text[]) AS matched_keys,
7541
7566
  coalesce((SELECT jsonb_agg(jsonb_build_object('key', _key, 'reason', reason, 'rowVersionStamp', row_write_version)) FROM fenced_rows), '[]'::jsonb) AS fenced_rows,
7567
+ coalesce((SELECT array_agg(_key) FROM same_version_conflicts), '{}'::text[]) AS conflict_keys,
7542
7568
  (SELECT count(*)::int FROM summary_delta) AS summary_delta_count,
7543
7569
  (SELECT count(*)::int FROM column_delta) AS column_delta_count`,
7544
7570
  [
@@ -7559,12 +7585,19 @@ async function completeRuntimeMapRowChunks(
7559
7585
  );
7560
7586
  const matchedKeys = new Set(rows[0]?.matched_keys ?? []);
7561
7587
  const chunkFencedRows = parseRuntimeMapRowsFencedRows(rows[0]?.fenced_rows);
7588
+ const chunkConflictKeys = rows[0]?.conflict_keys ?? [];
7562
7589
  // An exact-version replay that already produced the same terminal value is
7563
7590
  // accepted idempotently even when SQL has no physical mutation to apply.
7564
7591
  // Stale versions remain excluded through fenced_rows.
7565
7592
  const appliedCount = Number(rows[0]?.updated ?? 0);
7566
7593
  updated += appliedCount;
7567
7594
  fencedRows.push(...chunkFencedRows);
7595
+ conflictKeys.push(...chunkConflictKeys);
7596
+
7597
+ if (chunkConflictKeys.length > 0) {
7598
+ continue;
7599
+ }
7600
+
7568
7601
  if (matchedKeys.size === chunk.length) {
7569
7602
  continue;
7570
7603
  }
@@ -7582,8 +7615,9 @@ async function completeRuntimeMapRowChunks(
7582
7615
  );
7583
7616
  updated += repaired.updated;
7584
7617
  fencedRows.push(...repaired.fencedRows);
7618
+ conflictKeys.push(...repaired.conflictKeys);
7585
7619
  }
7586
- return { updated, fencedRows };
7620
+ return { updated, fencedRows, conflictKeys };
7587
7621
  }
7588
7622
 
7589
7623
  async function completeRuntimeMapRowChunksWithInputIndexRepair(
@@ -7593,6 +7627,7 @@ async function completeRuntimeMapRowChunksWithInputIndexRepair(
7593
7627
  ): Promise<RuntimeMapRowsMutationResult> {
7594
7628
  let updated = 0;
7595
7629
  const fencedRows: RuntimeMapRowsStaleRow[] = [];
7630
+ const conflictKeys: string[] = [];
7596
7631
  for (const chunk of input.chunks) {
7597
7632
  const chunkKeys = chunk.map((row) => row.key);
7598
7633
  const chunkInputIndexes = chunk.map((row) => row.input_index);
@@ -7612,6 +7647,7 @@ async function completeRuntimeMapRowChunksWithInputIndexRepair(
7612
7647
  const { rows } = await client.query<{
7613
7648
  _key?: string;
7614
7649
  fenced_rows?: unknown;
7650
+ conflict_keys?: string[];
7615
7651
  updated?: number;
7616
7652
  }>(
7617
7653
  `WITH updates AS (
@@ -7643,6 +7679,40 @@ async function completeRuntimeMapRowChunksWithInputIndexRepair(
7643
7679
  )
7644
7680
  ORDER BY target._key, (target._key = updates._key) DESC
7645
7681
  ),
7682
+ same_version_conflicts AS (
7683
+ SELECT updates.submitted_key AS _key
7684
+ FROM matched_updates AS updates
7685
+ JOIN ${sheetTable(session)} AS target
7686
+ ON target._key = updates.matched_key
7687
+ WHERE $13::bigint IS NOT NULL
7688
+ AND target._write_version = $13::bigint
7689
+ AND target._run_id = $5::text
7690
+ AND target._status IN ('enriched', 'failed')
7691
+ AND (
7692
+ EXISTS (
7693
+ SELECT 1
7694
+ FROM unnest($8::text[]) AS field_values(field)
7695
+ WHERE target._cell_meta -> field_values.field ->> 'runId' = $5::text
7696
+ )
7697
+ OR (
7698
+ coalesce(target._attempt_owner_run_id, target._run_id) = $10::text
7699
+ AND COALESCE(target._attempt_seq, 0) = $12::integer
7700
+ )
7701
+ )
7702
+ AND (
7703
+ target._status <> 'enriched'
7704
+ OR (${targetChangedPatchedCellSql})
7705
+ )
7706
+ -- First terminal wins: suppress conflict for same owner+epoch already-terminal row.
7707
+ -- Durable replay (omitted_terminal_replay) from same attempt epoch is idempotent.
7708
+ AND NOT (
7709
+ $10::text IS NOT NULL
7710
+ AND $12::integer IS NOT NULL
7711
+ AND target._status = 'enriched'
7712
+ AND coalesce(target._attempt_owner_run_id, target._run_id) = $10::text
7713
+ AND COALESCE(target._attempt_seq, 0) = $12::integer
7714
+ )
7715
+ ),
7646
7716
  applied_rows AS (
7647
7717
  UPDATE ${sheetTable(session)} AS target
7648
7718
  SET _status = 'enriched',
@@ -7660,31 +7730,14 @@ async function completeRuntimeMapRowChunksWithInputIndexRepair(
7660
7730
  FROM matched_updates AS updates, ${sheetTable(session)} AS prev
7661
7731
  WHERE target._key = updates.matched_key
7662
7732
  AND prev._key = target._key
7733
+ AND NOT EXISTS (SELECT 1 FROM same_version_conflicts)
7663
7734
  AND ($13::bigint IS NULL OR target._write_version = $13::bigint)
7664
7735
  AND ($13::bigint IS NOT NULL OR target._status <> 'enriched')
7665
7736
  AND NOT (${newerTerminalRuntimeSheetRowSql('target', '$11::timestamptz', '$12::integer', '$9::text', '$10::text')})
7666
7737
  AND (
7667
7738
  ($9::text IS NULL AND target._run_id = $5::text)
7668
7739
  OR ($9::text IS NOT NULL AND ${activeRuntimeSheetAttemptFenceSql('target', '$9::text', '$10::text', '$11::timestamptz', '$12::integer')})
7669
- OR ($9::text IS NOT NULL AND ${writableEnrichedAttemptSql})
7670
- )
7671
- AND NOT (
7672
- $13::bigint IS NOT NULL
7673
- AND target._write_version = $13::bigint
7674
- AND target._run_id = $5::text
7675
- AND target._status = 'enriched'
7676
- AND updates.submitted_key = target._key
7677
- AND (
7678
- EXISTS (
7679
- SELECT 1
7680
- FROM unnest($8::text[]) AS field_values(field)
7681
- WHERE target._cell_meta -> field_values.field ->> 'runId' = $5::text
7682
- )
7683
- OR (
7684
- $10::text IS NOT NULL
7685
- AND target._attempt_owner_run_id IS NOT DISTINCT FROM $10::text
7686
- )
7687
- )
7740
+ OR ($9::text IS NOT NULL AND ${writableEnrichedAttemptSql})
7688
7741
  )
7689
7742
  AND (
7690
7743
  target._status <> 'enriched'
@@ -7813,7 +7866,8 @@ async function completeRuntimeMapRowChunksWithInputIndexRepair(
7813
7866
  )
7814
7867
  SELECT
7815
7868
  (SELECT count(*)::int FROM applied_rows) AS updated,
7816
- coalesce((SELECT jsonb_agg(jsonb_build_object('key', _key, 'reason', reason, 'rowVersionStamp', row_write_version)) FROM fenced_rows), '[]'::jsonb) AS fenced_rows`,
7869
+ coalesce((SELECT jsonb_agg(jsonb_build_object('key', _key, 'reason', reason, 'rowVersionStamp', row_write_version)) FROM fenced_rows), '[]'::jsonb) AS fenced_rows,
7870
+ coalesce((SELECT array_agg(_key) FROM same_version_conflicts), '{}'::text[]) AS conflict_keys`,
7817
7871
  [
7818
7872
  chunkKeys,
7819
7873
  chunkInputIndexes,
@@ -7832,8 +7886,9 @@ async function completeRuntimeMapRowChunksWithInputIndexRepair(
7832
7886
  );
7833
7887
  updated += Number(rows[0]?.updated ?? 0);
7834
7888
  fencedRows.push(...parseRuntimeMapRowsFencedRows(rows[0]?.fenced_rows));
7889
+ conflictKeys.push(...(rows[0]?.conflict_keys ?? []));
7835
7890
  }
7836
- return { updated, fencedRows };
7891
+ return { updated, fencedRows, conflictKeys };
7837
7892
  }
7838
7893
 
7839
7894
  async function insertMissingCompletedMapRowChunks(
@@ -8029,6 +8084,7 @@ async function failRuntimeMapRowChunks(
8029
8084
  ): Promise<RuntimeMapRowsMutationResult> {
8030
8085
  let updated = 0;
8031
8086
  const fencedRows: RuntimeMapRowsStaleRow[] = [];
8087
+ const conflictKeys: string[] = [];
8032
8088
  for (const chunk of input.chunks) {
8033
8089
  const chunkKeys = chunk.map((row) => row.key);
8034
8090
  const chunkInputIndexes = chunk.map((row) => row.input_index);
@@ -8061,6 +8117,7 @@ async function failRuntimeMapRowChunks(
8061
8117
  const { rows } = await client.query<{
8062
8118
  updated?: number;
8063
8119
  fenced_rows?: unknown;
8120
+ conflict_keys?: string[];
8064
8121
  }>(
8065
8122
  `WITH updates AS (
8066
8123
  SELECT key_values._key,
@@ -8081,7 +8138,6 @@ async function failRuntimeMapRowChunks(
8081
8138
  matched_updates AS (
8082
8139
  SELECT DISTINCT ON (target._key)
8083
8140
  target._key AS matched_key,
8084
- updates._key AS submitted_key,
8085
8141
  updates.data_patch,
8086
8142
  updates.cell_meta_patch,
8087
8143
  updates.error
@@ -8095,6 +8151,42 @@ async function failRuntimeMapRowChunks(
8095
8151
  )
8096
8152
  ORDER BY target._key, (target._key = updates._key) DESC
8097
8153
  ),
8154
+ same_version_conflicts AS (
8155
+ SELECT updates.matched_key AS _key
8156
+ FROM matched_updates AS updates
8157
+ JOIN ${sheetTable(session)} AS target
8158
+ ON target._key = updates.matched_key
8159
+ WHERE $16::bigint IS NOT NULL
8160
+ AND target._write_version = $16::bigint
8161
+ AND target._run_id = $6::text
8162
+ AND target._status IN ('enriched', 'failed')
8163
+ AND (
8164
+ EXISTS (
8165
+ SELECT 1
8166
+ FROM unnest($9::text[]) AS field_values(field)
8167
+ WHERE target._cell_meta -> field_values.field ->> 'runId' = $6::text
8168
+ )
8169
+ OR (
8170
+ coalesce(target._attempt_owner_run_id, target._run_id) = $12::text
8171
+ AND COALESCE(target._attempt_seq, 0) = $14::integer
8172
+ )
8173
+ )
8174
+ AND (
8175
+ target._status <> 'failed'
8176
+ OR target._error IS DISTINCT FROM updates.error
8177
+ OR (${targetChangedPatchedCellSql})
8178
+ )
8179
+ -- Same-owner terminal replay is not a conflict. Normal replay
8180
+ -- preserves the first value below; explicit force-failure may update it.
8181
+ -- Different owners and attempt epochs still conflict.
8182
+ AND NOT (
8183
+ $12::text IS NOT NULL
8184
+ AND $14::integer IS NOT NULL
8185
+ AND target._status IN ('failed', 'enriched')
8186
+ AND coalesce(target._attempt_owner_run_id, target._run_id) = $12::text
8187
+ AND COALESCE(target._attempt_seq, 0) = $14::integer
8188
+ )
8189
+ ),
8098
8190
  applied_rows AS (
8099
8191
  UPDATE ${sheetTable(session)} AS target
8100
8192
  SET _status = 'failed',
@@ -8111,6 +8203,7 @@ async function failRuntimeMapRowChunks(
8111
8203
  _cell_meta = ${mergeRuntimeCellMetaPatchSql('target._cell_meta', 'updates.cell_meta_patch')}${input.physicalUpdateSetSql}
8112
8204
  FROM matched_updates AS updates, ${sheetTable(session)} AS prev
8113
8205
  WHERE target._key = updates.matched_key
8206
+ AND NOT EXISTS (SELECT 1 FROM same_version_conflicts)
8114
8207
  AND ($16::bigint IS NULL OR target._write_version = $16::bigint)
8115
8208
  AND prev._key = target._key
8116
8209
  AND ($15::boolean OR target._status <> 'enriched')
@@ -8119,16 +8212,15 @@ async function failRuntimeMapRowChunks(
8119
8212
  ($11::text IS NULL AND target._run_id = $6::text)
8120
8213
  OR ($11::text IS NOT NULL AND ${activeRuntimeSheetAttemptFenceSql('target', '$11::text', '$12::text', '$13::timestamptz', '$14::integer')})
8121
8214
  )
8122
- -- Normal replay preserves the first terminal value for an exact
8123
- -- same-version key. Force-failure is the explicit teardown escape
8124
- -- hatch and may still overwrite it.
8215
+ -- First terminal wins for failed rows too, BUT only when NOT force-failing.
8216
+ -- Normal replay: no-op when already-failed OR already-enriched from same owner+epoch.
8125
8217
  AND NOT (
8126
8218
  $15::boolean = FALSE
8127
- AND $16::bigint IS NOT NULL
8128
- AND target._write_version = $16::bigint
8129
- AND target._run_id = $6::text
8219
+ AND $12::text IS NOT NULL
8220
+ AND $14::integer IS NOT NULL
8130
8221
  AND target._status IN ('failed', 'enriched')
8131
- AND updates.submitted_key = target._key
8222
+ AND coalesce(target._attempt_owner_run_id, target._run_id) = $12::text
8223
+ AND COALESCE(target._attempt_seq, 0) = $14::integer
8132
8224
  )
8133
8225
  RETURNING target._key, prev._status AS prev_status
8134
8226
  ),
@@ -8225,7 +8317,8 @@ async function failRuntimeMapRowChunks(
8225
8317
  )
8226
8318
  SELECT
8227
8319
  (SELECT count(*)::int FROM applied_rows) AS updated,
8228
- coalesce((SELECT jsonb_agg(jsonb_build_object('key', _key, 'reason', reason, 'rowVersionStamp', row_write_version)) FROM fenced_rows), '[]'::jsonb) AS fenced_rows`,
8320
+ coalesce((SELECT jsonb_agg(jsonb_build_object('key', _key, 'reason', reason, 'rowVersionStamp', row_write_version)) FROM fenced_rows), '[]'::jsonb) AS fenced_rows,
8321
+ coalesce((SELECT array_agg(_key) FROM same_version_conflicts), '{}'::text[]) AS conflict_keys`,
8229
8322
  [
8230
8323
  chunkKeys,
8231
8324
  chunkInputIndexes,
@@ -8247,10 +8340,12 @@ async function failRuntimeMapRowChunks(
8247
8340
  );
8248
8341
  const appliedCount = Number(rows[0]?.updated ?? 0);
8249
8342
  const chunkFencedRows = parseRuntimeMapRowsFencedRows(rows[0]?.fenced_rows);
8343
+ const chunkConflictKeys = rows[0]?.conflict_keys ?? [];
8250
8344
  updated += appliedCount;
8251
8345
  fencedRows.push(...chunkFencedRows);
8346
+ conflictKeys.push(...chunkConflictKeys);
8252
8347
  }
8253
- return { updated, fencedRows };
8348
+ return { updated, fencedRows, conflictKeys };
8254
8349
  }
8255
8350
 
8256
8351
  /**
@@ -8461,9 +8556,10 @@ export async function completeRuntimeMapRows(
8461
8556
  return {
8462
8557
  updated: updated.updated + inserted.inserted,
8463
8558
  fencedRows: updated.fencedRows,
8559
+ conflictKeys: updated.conflictKeys,
8464
8560
  };
8465
8561
  })()
8466
- : { updated: 0, fencedRows: [] };
8562
+ : { updated: 0, fencedRows: [], conflictKeys: [] };
8467
8563
  const failed =
8468
8564
  failedRows.length > 0
8469
8565
  ? await failRuntimeMapRowChunks(client, session, {
@@ -8482,7 +8578,17 @@ export async function completeRuntimeMapRows(
8482
8578
  input.tableNamespace,
8483
8579
  ),
8484
8580
  })
8485
- : { updated: 0, fencedRows: [] };
8581
+ : { updated: 0, fencedRows: [], conflictKeys: [] };
8582
+ const conflictKeys = [...completed.conflictKeys, ...failed.conflictKeys];
8583
+ if (conflictKeys.length > 0) {
8584
+ throw new Error(
8585
+ `Runtime Sheet received the same write version with a different terminal payload for row(s): ${[
8586
+ ...new Set(conflictKeys),
8587
+ ]
8588
+ .slice(0, 10)
8589
+ .join(', ')}.`,
8590
+ );
8591
+ }
8486
8592
  return classifyRuntimeMapRowsWrite({
8487
8593
  submittedKeys: uniqueRows.keys(),
8488
8594
  updated: completed.updated + failed.updated,
@@ -41,11 +41,11 @@ export function releasableRuntimeSheetAttemptFenceSql(
41
41
  }
42
42
 
43
43
  /**
44
- * Predicate for callers that need to identify a terminal row from the same
45
- * attempt owner + epoch. Runtime map persistence now enforces first-terminal
46
- * idempotency inline because its write-version and row-key context are also
47
- * required; keep this helper exported for compatibility with external
48
- * conformance checks and integrations.
44
+ * Predicate for durable replay idempotency: matches rows that are already
45
+ * terminal (enriched or failed) from the same attempt owner + epoch. Used to
46
+ * suppress same-version conflicts when a worker restarts and re-runs map rows
47
+ * that it already completed. First terminal state wins; payload/error drift on
48
+ * replay is accepted as a no-op rather than rejected as a conflict.
49
49
  */
50
50
  export function sameOwnerTerminalAttemptEpochSql(
51
51
  tableAlias: string,
package/dist/cli/index.js CHANGED
@@ -3051,7 +3051,7 @@ var SDK_RELEASE = {
3051
3051
  // getters keep their established compatibility behavior.
3052
3052
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
3053
3053
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
3054
- version: "0.3.187",
3054
+ version: "0.3.188",
3055
3055
  updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.",
3056
3056
  packageCapabilities: {
3057
3057
  updatePreferences: 1
@@ -3046,7 +3046,7 @@ var SDK_RELEASE = {
3046
3046
  // getters keep their established compatibility behavior.
3047
3047
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
3048
3048
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
3049
- version: "0.3.187",
3049
+ version: "0.3.188",
3050
3050
  updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.",
3051
3051
  packageCapabilities: {
3052
3052
  updatePreferences: 1
package/dist/index.js CHANGED
@@ -864,7 +864,7 @@ var SDK_RELEASE = {
864
864
  // getters keep their established compatibility behavior.
865
865
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
866
866
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
867
- version: "0.3.187",
867
+ version: "0.3.188",
868
868
  updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.",
869
869
  packageCapabilities: {
870
870
  updatePreferences: 1
package/dist/index.mjs CHANGED
@@ -768,7 +768,7 @@ var SDK_RELEASE = {
768
768
  // getters keep their established compatibility behavior.
769
769
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
770
770
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
771
- version: "0.3.187",
771
+ version: "0.3.188",
772
772
  updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.",
773
773
  packageCapabilities: {
774
774
  updatePreferences: 1
@@ -149,7 +149,7 @@ type SdkRelease = {
149
149
  supportPolicy: SdkSupportPolicy;
150
150
  };
151
151
  declare const SDK_RELEASE: {
152
- readonly version: "0.3.187";
152
+ readonly version: "0.3.188";
153
153
  readonly updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.";
154
154
  readonly packageCapabilities: {
155
155
  readonly updatePreferences: 1;
package/dist/release.d.ts CHANGED
@@ -149,7 +149,7 @@ type SdkRelease = {
149
149
  supportPolicy: SdkSupportPolicy;
150
150
  };
151
151
  declare const SDK_RELEASE: {
152
- readonly version: "0.3.187";
152
+ readonly version: "0.3.188";
153
153
  readonly updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.";
154
154
  readonly packageCapabilities: {
155
155
  readonly updatePreferences: 1;
package/dist/release.js CHANGED
@@ -74,7 +74,7 @@ var SDK_RELEASE = {
74
74
  // getters keep their established compatibility behavior.
75
75
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
76
76
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
77
- version: "0.3.187",
77
+ version: "0.3.188",
78
78
  updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.",
79
79
  packageCapabilities: {
80
80
  updatePreferences: 1
package/dist/release.mjs CHANGED
@@ -48,7 +48,7 @@ var SDK_RELEASE = {
48
48
  // getters keep their established compatibility behavior.
49
49
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
50
50
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
51
- version: "0.3.187",
51
+ version: "0.3.188",
52
52
  updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.",
53
53
  packageCapabilities: {
54
54
  updatePreferences: 1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.3.187",
3
+ "version": "0.3.188",
4
4
  "description": "GTM data CLI and TypeScript SDK for coding agents",
5
5
  "license": "MIT",
6
6
  "homepage": "https://code.deepline.com",