@reddoorla/maintenance 0.97.0 → 0.98.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.
Files changed (130) hide show
  1. package/dist/{announce-5ZD2SBGD.js → announce-2FH2KBQQ.js} +4 -4
  2. package/dist/{bump-deps-775VLDYB.js → bump-deps-54K4ECP3.js} +2 -2
  3. package/dist/{chunk-YXWBM26P.js → chunk-3YALLSBF.js} +2 -2
  4. package/dist/{chunk-LAJ3FF54.js → chunk-5UI4FERC.js} +16 -5
  5. package/dist/chunk-5UI4FERC.js.map +1 -0
  6. package/dist/{chunk-VMGHQUTV.js → chunk-B2BRFWJY.js} +2 -2
  7. package/dist/{chunk-IOTOOTII.js → chunk-CP6MVZ5X.js} +16 -1
  8. package/dist/{chunk-IOTOOTII.js.map → chunk-CP6MVZ5X.js.map} +1 -1
  9. package/dist/{chunk-L3S3QZXL.js → chunk-DXZRWW2F.js} +64 -9
  10. package/dist/chunk-DXZRWW2F.js.map +1 -0
  11. package/dist/chunk-DZZ5KRTB.js +23 -0
  12. package/dist/chunk-DZZ5KRTB.js.map +1 -0
  13. package/dist/{chunk-U2ZI6G7H.js → chunk-GRF2RYEO.js} +2 -2
  14. package/dist/{chunk-QFSL2L2B.js → chunk-L5F3WYIY.js} +81 -31
  15. package/dist/chunk-L5F3WYIY.js.map +1 -0
  16. package/dist/{chunk-MMSMZC7S.js → chunk-MNRWHSFS.js} +2 -2
  17. package/dist/{chunk-PARKXDXI.js → chunk-ORTOMEYR.js} +2 -2
  18. package/dist/{chunk-FP6HOW6J.js → chunk-S2PLX4XK.js} +2 -2
  19. package/dist/{chunk-A5QKNOXW.js → chunk-TTT6HYPW.js} +47 -10
  20. package/dist/chunk-TTT6HYPW.js.map +1 -0
  21. package/dist/{chunk-BIYQN7FH.js → chunk-VCPTOZ56.js} +31 -1
  22. package/dist/{chunk-BIYQN7FH.js.map → chunk-VCPTOZ56.js.map} +1 -1
  23. package/dist/{chunk-LDHRIQUW.js → chunk-ZEE5WQYU.js} +2 -2
  24. package/dist/cli/bin.js +27 -27
  25. package/dist/cli/commands/audit.js +10 -10
  26. package/dist/client-3XGSZMLU.js +11 -0
  27. package/dist/{convert-to-pnpm-KLWA53N5.js → convert-to-pnpm-U2CRNKNY.js} +2 -2
  28. package/dist/{db-II5WAK4D.js → db-7XYTBIPH.js} +20 -13
  29. package/dist/db-7XYTBIPH.js.map +1 -0
  30. package/dist/{deadletter-G4M3HSVM.js → deadletter-A7RQVNG3.js} +8 -4
  31. package/dist/{digest-CWKK2DMR.js → digest-AMXRGWZA.js} +20 -20
  32. package/dist/{digest-collectors-CYTN2EUX.js → digest-collectors-CX2NCGS7.js} +7 -7
  33. package/dist/{ensure-site-2QZHFVQ3.js → ensure-site-F2WFCWWA.js} +2 -2
  34. package/dist/{forms-notify-target-X6UEOI2M.js → forms-notify-target-DK5R326N.js} +3 -3
  35. package/dist/{github-signals-QIRTHHET.js → github-signals-RGRPYTUB.js} +6 -6
  36. package/dist/{header-image-H7ZJAV26.js → header-image-43ZIEJKP.js} +3 -3
  37. package/dist/{health-endpoint-BJ6BKRHF.js → health-endpoint-K5LXDPGE.js} +2 -2
  38. package/dist/{health-mirror-KU3G7V2Y.js → health-mirror-WZUHWCCV.js} +3 -3
  39. package/dist/index.js +10 -11
  40. package/dist/index.js.map +1 -1
  41. package/dist/{init-GOWDA5QS.js → init-OFITGMOP.js} +5 -5
  42. package/dist/{launch-7N3X2MXB.js → launch-AJY3TGTQ.js} +7 -7
  43. package/dist/{lighthouse-52WSPZ5D.js → lighthouse-RFMFREEX.js} +2 -2
  44. package/dist/{match-harness-GBCDSVPS.js → match-harness-CFELJA57.js} +2 -2
  45. package/dist/migrate-EZL55AWA.js +7 -0
  46. package/dist/{onboard-JQQIMQW7.js → onboard-F7RTLMDZ.js} +2 -2
  47. package/dist/{orchestrate-L2A5BZFY.js → orchestrate-SAIFXZIW.js} +5 -5
  48. package/dist/{pipeline-UUPXOS2J.js → pipeline-TCSNVDQZ.js} +3 -3
  49. package/dist/{preflight-O7Q6FWHU.js → preflight-6XGVVO2U.js} +7 -7
  50. package/dist/{prismic-ci-NAPWOXXD.js → prismic-ci-XMX7A5E4.js} +2 -2
  51. package/dist/{prismic-models-LL6ORTQ2.js → prismic-models-7GKFAAFA.js} +4 -4
  52. package/dist/prospect/types.d.ts +44 -2
  53. package/dist/prospect/types.js +1 -1
  54. package/dist/{prospect-audit-FMPGQIRQ.js → prospect-audit-KWP5P5U2.js} +37 -5
  55. package/dist/prospect-audit-KWP5P5U2.js.map +1 -0
  56. package/dist/{render-MHUUHPLN.js → render-NWUAX6RN.js} +3 -3
  57. package/dist/{renovate-dispatch-2IHAZDVA.js → renovate-dispatch-ZZQXT7DA.js} +2 -2
  58. package/dist/replay-HB44TAII.js +77 -0
  59. package/dist/replay-HB44TAII.js.map +1 -0
  60. package/dist/{report-IAHXN5CW.js → report-MVPWS7WP.js} +13 -13
  61. package/dist/{report-mirror-4QM57LMS.js → report-mirror-O6IDFDWU.js} +3 -3
  62. package/dist/{roster-GDNSIRAK.js → roster-EJ3ILR52.js} +2 -2
  63. package/dist/{screenouts-7P5GOZMV.js → screenouts-7NQUWHOJ.js} +14 -1
  64. package/dist/screenouts-7NQUWHOJ.js.map +1 -0
  65. package/dist/{self-updating-ZUWPGYUP.js → self-updating-KX5FMGEO.js} +2 -2
  66. package/dist/{selftest-G4W3LIBQ.js → selftest-KABY2CYJ.js} +5 -5
  67. package/dist/{site-mirror-WR7ACXOV.js → site-mirror-TKTDWYP3.js} +3 -3
  68. package/dist/{smoke-suite-WAUPI6EJ.js → smoke-suite-QQMV7TNA.js} +2 -2
  69. package/dist/{submissions-JDZP35JM.js → submissions-4I66W6QK.js} +11 -1
  70. package/dist/{submissions-JDZP35JM.js.map → submissions-4I66W6QK.js.map} +1 -1
  71. package/dist/{submissions-2SHCFOKD.js → submissions-K5UXGZUN.js} +4 -4
  72. package/dist/{svelte-codemods-VOLOKKFI.js → svelte-codemods-YFN4TEGV.js} +2 -2
  73. package/dist/{sync-configs-OXSWKVZ2.js → sync-configs-HBXS3D7C.js} +2 -2
  74. package/dist/{upgrade-EVAL3JTO.js → upgrade-XOJMDIT4.js} +2 -2
  75. package/package.json +1 -1
  76. package/dist/chunk-A5QKNOXW.js.map +0 -1
  77. package/dist/chunk-L3S3QZXL.js.map +0 -1
  78. package/dist/chunk-LAJ3FF54.js.map +0 -1
  79. package/dist/chunk-QFSL2L2B.js.map +0 -1
  80. package/dist/client-DGTO65WB.js +0 -11
  81. package/dist/db-II5WAK4D.js.map +0 -1
  82. package/dist/migrate-XZ7U5DFC.js +0 -7
  83. package/dist/prospect-audit-FMPGQIRQ.js.map +0 -1
  84. package/dist/replay-4FVFOMHH.js +0 -54
  85. package/dist/replay-4FVFOMHH.js.map +0 -1
  86. package/dist/screenouts-7P5GOZMV.js.map +0 -1
  87. /package/dist/{announce-5ZD2SBGD.js.map → announce-2FH2KBQQ.js.map} +0 -0
  88. /package/dist/{bump-deps-775VLDYB.js.map → bump-deps-54K4ECP3.js.map} +0 -0
  89. /package/dist/{chunk-YXWBM26P.js.map → chunk-3YALLSBF.js.map} +0 -0
  90. /package/dist/{chunk-VMGHQUTV.js.map → chunk-B2BRFWJY.js.map} +0 -0
  91. /package/dist/{chunk-U2ZI6G7H.js.map → chunk-GRF2RYEO.js.map} +0 -0
  92. /package/dist/{chunk-MMSMZC7S.js.map → chunk-MNRWHSFS.js.map} +0 -0
  93. /package/dist/{chunk-PARKXDXI.js.map → chunk-ORTOMEYR.js.map} +0 -0
  94. /package/dist/{chunk-FP6HOW6J.js.map → chunk-S2PLX4XK.js.map} +0 -0
  95. /package/dist/{chunk-LDHRIQUW.js.map → chunk-ZEE5WQYU.js.map} +0 -0
  96. /package/dist/{client-DGTO65WB.js.map → client-3XGSZMLU.js.map} +0 -0
  97. /package/dist/{convert-to-pnpm-KLWA53N5.js.map → convert-to-pnpm-U2CRNKNY.js.map} +0 -0
  98. /package/dist/{deadletter-G4M3HSVM.js.map → deadletter-A7RQVNG3.js.map} +0 -0
  99. /package/dist/{digest-CWKK2DMR.js.map → digest-AMXRGWZA.js.map} +0 -0
  100. /package/dist/{digest-collectors-CYTN2EUX.js.map → digest-collectors-CX2NCGS7.js.map} +0 -0
  101. /package/dist/{ensure-site-2QZHFVQ3.js.map → ensure-site-F2WFCWWA.js.map} +0 -0
  102. /package/dist/{forms-notify-target-X6UEOI2M.js.map → forms-notify-target-DK5R326N.js.map} +0 -0
  103. /package/dist/{github-signals-QIRTHHET.js.map → github-signals-RGRPYTUB.js.map} +0 -0
  104. /package/dist/{header-image-H7ZJAV26.js.map → header-image-43ZIEJKP.js.map} +0 -0
  105. /package/dist/{health-endpoint-BJ6BKRHF.js.map → health-endpoint-K5LXDPGE.js.map} +0 -0
  106. /package/dist/{health-mirror-KU3G7V2Y.js.map → health-mirror-WZUHWCCV.js.map} +0 -0
  107. /package/dist/{init-GOWDA5QS.js.map → init-OFITGMOP.js.map} +0 -0
  108. /package/dist/{launch-7N3X2MXB.js.map → launch-AJY3TGTQ.js.map} +0 -0
  109. /package/dist/{lighthouse-52WSPZ5D.js.map → lighthouse-RFMFREEX.js.map} +0 -0
  110. /package/dist/{match-harness-GBCDSVPS.js.map → match-harness-CFELJA57.js.map} +0 -0
  111. /package/dist/{migrate-XZ7U5DFC.js.map → migrate-EZL55AWA.js.map} +0 -0
  112. /package/dist/{onboard-JQQIMQW7.js.map → onboard-F7RTLMDZ.js.map} +0 -0
  113. /package/dist/{orchestrate-L2A5BZFY.js.map → orchestrate-SAIFXZIW.js.map} +0 -0
  114. /package/dist/{pipeline-UUPXOS2J.js.map → pipeline-TCSNVDQZ.js.map} +0 -0
  115. /package/dist/{preflight-O7Q6FWHU.js.map → preflight-6XGVVO2U.js.map} +0 -0
  116. /package/dist/{prismic-ci-NAPWOXXD.js.map → prismic-ci-XMX7A5E4.js.map} +0 -0
  117. /package/dist/{prismic-models-LL6ORTQ2.js.map → prismic-models-7GKFAAFA.js.map} +0 -0
  118. /package/dist/{render-MHUUHPLN.js.map → render-NWUAX6RN.js.map} +0 -0
  119. /package/dist/{renovate-dispatch-2IHAZDVA.js.map → renovate-dispatch-ZZQXT7DA.js.map} +0 -0
  120. /package/dist/{report-IAHXN5CW.js.map → report-MVPWS7WP.js.map} +0 -0
  121. /package/dist/{report-mirror-4QM57LMS.js.map → report-mirror-O6IDFDWU.js.map} +0 -0
  122. /package/dist/{roster-GDNSIRAK.js.map → roster-EJ3ILR52.js.map} +0 -0
  123. /package/dist/{self-updating-ZUWPGYUP.js.map → self-updating-KX5FMGEO.js.map} +0 -0
  124. /package/dist/{selftest-G4W3LIBQ.js.map → selftest-KABY2CYJ.js.map} +0 -0
  125. /package/dist/{site-mirror-WR7ACXOV.js.map → site-mirror-TKTDWYP3.js.map} +0 -0
  126. /package/dist/{smoke-suite-WAUPI6EJ.js.map → smoke-suite-QQMV7TNA.js.map} +0 -0
  127. /package/dist/{submissions-2SHCFOKD.js.map → submissions-K5UXGZUN.js.map} +0 -0
  128. /package/dist/{svelte-codemods-VOLOKKFI.js.map → svelte-codemods-YFN4TEGV.js.map} +0 -0
  129. /package/dist/{sync-configs-OXSWKVZ2.js.map → sync-configs-HBXS3D7C.js.map} +0 -0
  130. /package/dist/{upgrade-EVAL3JTO.js.map → upgrade-XOJMDIT4.js.map} +0 -0
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-DYTYPKFN.js";
9
9
  import {
10
10
  resolveSites
11
- } from "./chunk-FP6HOW6J.js";
11
+ } from "./chunk-S2PLX4XK.js";
12
12
  import {
13
13
  convertToPnpm
14
14
  } from "./chunk-AYDQQBNG.js";
@@ -51,4 +51,4 @@ async function runConvertToPnpmCommand(site, opts) {
51
51
  export {
52
52
  runConvertToPnpmCommand
53
53
  };
54
- //# sourceMappingURL=convert-to-pnpm-KLWA53N5.js.map
54
+ //# sourceMappingURL=convert-to-pnpm-U2CRNKNY.js.map
@@ -10,9 +10,9 @@ function freezeGuardsDbWrite(action, force, authoritative) {
10
10
  }
11
11
  async function runDbCommand(action, opts, deps = {}) {
12
12
  if (action === "migrate") {
13
- const { readDbConfig } = await import("./client-DGTO65WB.js");
13
+ const { readDbConfig } = await import("./client-3XGSZMLU.js");
14
14
  const cfg = opts.url ? { url: opts.url } : readDbConfig();
15
- const { runMigrations } = await import("./migrate-XZ7U5DFC.js");
15
+ const { runMigrations } = await import("./migrate-EZL55AWA.js");
16
16
  const { createClient } = await import("@libsql/client");
17
17
  const client = createClient(cfg.url === ":memory:" ? { url: ":memory:" } : cfg);
18
18
  const ran = await runMigrations(client);
@@ -22,7 +22,7 @@ async function runDbCommand(action, opts, deps = {}) {
22
22
  };
23
23
  }
24
24
  if (action === "replay-deadletters") {
25
- const { readDbConfig, openDb } = await import("./client-DGTO65WB.js");
25
+ const { readDbConfig, openDb } = await import("./client-3XGSZMLU.js");
26
26
  if (opts.abandon !== void 0) {
27
27
  const reason = (opts.reason ?? "").trim();
28
28
  if (reason === "") {
@@ -33,7 +33,7 @@ async function runDbCommand(action, opts, deps = {}) {
33
33
  }
34
34
  const by = opts.by ?? process.env.OPERATOR_EMAIL ?? "operator";
35
35
  const db2 = await openDb(opts.url ? { url: opts.url } : readDbConfig());
36
- const { abandonDeadLetters } = await import("./deadletter-G4M3HSVM.js");
36
+ const { abandonDeadLetters } = await import("./deadletter-A7RQVNG3.js");
37
37
  const target = opts.abandon.startsWith("dl_") ? { id: opts.abandon } : { slug: opts.abandon };
38
38
  const ids = await abandonDeadLetters(db2, { ...target, by, reason, now: /* @__PURE__ */ new Date() });
39
39
  const lines2 = [
@@ -59,13 +59,13 @@ async function runDbCommand(action, opts, deps = {}) {
59
59
  findRecentDuplicateSubmissions,
60
60
  listRecentSubmissionsForEmail,
61
61
  markSubmissionsSpamRetro
62
- } = await import("./submissions-JDZP35JM.js");
62
+ } = await import("./submissions-4I66W6QK.js");
63
63
  const { makeNotify } = await import("./notify-K3WVH3DM.js");
64
64
  const { classifySpam } = await import("./spam-classifier-T5H4VTP6.js");
65
65
  const { forwardNewsletterToWebhook } = await import("./webhook-XM6EV4XT.js");
66
66
  const { addMailchimpMember, mailchimpTagsFor } = await import("./mailchimp-XYCTEEJY.js");
67
67
  const { defaultResendClient } = await import("./resend-G2BJMAQV.js");
68
- const { replayDeadLetters } = await import("./replay-4FVFOMHH.js");
68
+ const { replayDeadLetters } = await import("./replay-HB44TAII.js");
69
69
  let send = null;
70
70
  try {
71
71
  send = defaultResendClient().send;
@@ -104,15 +104,22 @@ async function runDbCommand(action, opts, deps = {}) {
104
104
  (r) => `replayed ${r.id} \u2192 ${r.outcome}${r.submissionId ? ` (${r.submissionId})` : ""}`
105
105
  ),
106
106
  ...result.stillFailing.map((r) => `still failing ${r.id}: ${r.error}`),
107
- `DEADLETTER_REPLAY replayed=${result.replayed.length} still_failing=${result.stillFailing.length}`
107
+ ...result.unmarked.map(
108
+ (r) => `UNMARKED ${r.id}: re-ingested as ${r.submissionId ?? "(no submission)"} \u2192 ${r.outcome}, but the terminal mark could NOT be written (${r.error}). The row is still queued, so replaying again before it is reconciled will mint a DUPLICATE of this lead.`
109
+ ),
110
+ ...result.unreadable.map(
111
+ (r) => `UNREADABLE ${r.id} (site '${r.siteSlug}', received ${r.receivedAt}): ${r.error}. The row is untouched \u2014 repair the stored JSON, or retire it with \`db replay-deadletters --abandon ${r.id} --reason "\u2026"\`.`
112
+ ),
113
+ `DEADLETTER_REPLAY replayed=${result.replayed.length} still_failing=${result.stillFailing.length} unmarked=${result.unmarked.length} unreadable=${result.unreadable.length}`
108
114
  ];
109
- return { output: lines.join("\n"), code: result.stillFailing.length > 0 ? 1 : 0 };
115
+ const owed = result.stillFailing.length + result.unmarked.length + result.unreadable.length > 0;
116
+ return { output: lines.join("\n"), code: owed ? 1 : 0 };
110
117
  }
111
118
  if (action === "import-airtable" || action === "parity" || action === "sync") {
112
119
  const { TURSO_IS_AUTHORITATIVE } = await import("./freeze-GUQ43EAU.js");
113
120
  const refused = freezeGuardsDbWrite(action, opts.force === true, TURSO_IS_AUTHORITATIVE);
114
121
  if (refused) return refused;
115
- const { readDbConfig, openDb } = await import("./client-DGTO65WB.js");
122
+ const { readDbConfig, openDb } = await import("./client-3XGSZMLU.js");
116
123
  const db = await openDb(opts.url ? { url: opts.url } : readDbConfig());
117
124
  const { openBase, readAirtableConfig } = await import("./client-EPVO3FEH.js");
118
125
  const base = openBase(readAirtableConfig());
@@ -175,7 +182,7 @@ async function runDbCommand(action, opts, deps = {}) {
175
182
  return { output: formatParityResult(result), code: result.mismatches.length > 0 ? 1 : 0 };
176
183
  }
177
184
  if (action === "backfill-header-images") {
178
- const { readDbConfig, openDb } = await import("./client-DGTO65WB.js");
185
+ const { readDbConfig, openDb } = await import("./client-3XGSZMLU.js");
179
186
  const db = await openDb(opts.url ? { url: opts.url } : readDbConfig());
180
187
  const { openBase, readAirtableConfig } = await import("./client-EPVO3FEH.js");
181
188
  const base = openBase(readAirtableConfig());
@@ -197,7 +204,7 @@ async function runDbCommand(action, opts, deps = {}) {
197
204
  return { output: formatBackfillResult(result), code: result.failed.length > 0 ? 1 : 0 };
198
205
  }
199
206
  if (action === "backfill-digest-state") {
200
- const { readDbConfig, openDb } = await import("./client-DGTO65WB.js");
207
+ const { readDbConfig, openDb } = await import("./client-3XGSZMLU.js");
201
208
  const db = await openDb(opts.url ? { url: opts.url } : readDbConfig());
202
209
  const { readDigestState: readTurso, writeDigestState: writeTurso } = await import("./digest-state-ZDPKR7MW.js");
203
210
  const existing = await readTurso(db);
@@ -222,7 +229,7 @@ async function runDbCommand(action, opts, deps = {}) {
222
229
  };
223
230
  }
224
231
  if (action === "dump") {
225
- const { readDbConfig } = await import("./client-DGTO65WB.js");
232
+ const { readDbConfig } = await import("./client-3XGSZMLU.js");
226
233
  const cfg = opts.url ? { url: opts.url } : readDbConfig();
227
234
  const { createClient } = await import("@libsql/client");
228
235
  const client = createClient(cfg.url === ":memory:" ? { url: ":memory:" } : cfg);
@@ -375,4 +382,4 @@ export {
375
382
  freezeGuardsDbWrite,
376
383
  runDbCommand
377
384
  };
378
- //# sourceMappingURL=db-II5WAK4D.js.map
385
+ //# sourceMappingURL=db-7XYTBIPH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/db.ts"],"sourcesContent":["export type DbCommandOptions = {\n /** Override the libSQL url (tests use \":memory:\"); otherwise read from env. */\n url?: string;\n /** verify-dump: path to the dump file to load into a scratch engine. */\n file?: string;\n /** usage: org slug override; defaults to TURSO_ORG, else discovered. */\n org?: string;\n /** import-airtable / sync: run despite the freeze — a deliberate\n * rollback-window converge from the frozen Airtable shadow. */\n force?: boolean;\n /** replay-deadletters: abandon this slug's queued leads (or one `dl_…` row id)\n * as resolved-by-decision instead of replaying them (#786). */\n abandon?: string;\n /** replay-deadletters --abandon: why. Required — an undocumented write-off of\n * a client's leads is the thing this is meant to stop being necessary. */\n reason?: string;\n /** replay-deadletters --abandon: who decided. Defaults to OPERATOR_EMAIL. */\n by?: string;\n cwd?: string;\n verbose?: boolean;\n};\n\n/** Injected seams — deliberately NOT part of DbCommandOptions, which is the set\n * of things a shell can type (a registration gate asserts exactly that). The\n * platform token lives here rather than on a flag because a secret passed on\n * argv is readable from `ps`. */\nexport type DbCommandDeps = {\n /** Tests pass \"\" to exercise the unconfigured path, so the implementation\n * must use `?? env` and never `|| env`. */\n platformToken?: string;\n fetchImpl?: (url: string, init?: RequestInit) => Promise<Response>;\n now?: Date;\n /** restore: auth token for the TARGET database; defaults to\n * TURSO_RESTORE_AUTH_TOKEN. Deliberately does NOT fall back to the ambient\n * TURSO_AUTH_TOKEN — that one belongs to production, and inheriting it would\n * undo the whole point of making --url explicit. */\n restoreAuthToken?: string;\n};\n\n/** #643 (the freeze): the scheduled import retired with the flip, but the\n * MANUAL import survives as the rollback-window converge tool — and run out of\n * habit it would overwrite authoritative Turso rows with the frozen Airtable\n * archive, including any post-flip write whose best-effort shadow was\n * swallowed. So the writing actions refuse under the freeze unless the\n * operator says `--force`. `parity` stays unguarded: it only compares, and\n * \"did the shadow drift?\" is exactly the rollback-window question.\n *\n * Pure and exported so the test injects BOTH switch states; `runDbCommand`\n * passes the shipped constant. Returns the refusal, or null to proceed. */\nexport function freezeGuardsDbWrite(\n action: string,\n force: boolean,\n authoritative: boolean,\n): { output: string; code: number } | null {\n if (!authoritative) return null;\n if (action !== \"import-airtable\" && action !== \"sync\") return null;\n if (force) return null;\n return {\n output:\n `db ${action} refused: TURSO_IS_AUTHORITATIVE is on (the freeze, 2026-08-31). ` +\n `An import now OVERWRITES authoritative Turso rows with the frozen Airtable ` +\n `archive. Pass --force only for a deliberate rollback-window converge.`,\n code: 1,\n };\n}\n\n/** `db <action>` — migrate | replay-deadletters | import-airtable | parity | sync | dump | verify-dump. The db layer is imported\n * dynamically so a non-db CLI invocation (and `--help`) never loads\n * @libsql/client. Config is resolved inside each branch so an unknown action\n * returns without needing any Turso env. */\nexport async function runDbCommand(\n action: string,\n opts: DbCommandOptions,\n deps: DbCommandDeps = {},\n): Promise<{ output: string; code: number }> {\n if (action === \"migrate\") {\n const { readDbConfig } = await import(\"../../db/client.js\");\n const cfg = opts.url ? { url: opts.url } : readDbConfig();\n const { runMigrations } = await import(\"../../db/migrate.js\");\n const { createClient } = await import(\"@libsql/client\");\n const client = createClient(cfg.url === \":memory:\" ? { url: \":memory:\" } : cfg);\n const ran = await runMigrations(client);\n return {\n output: ran.length ? `Applied migrations: ${ran.join(\", \")}` : \"Already up to date.\",\n code: 0,\n };\n }\n\n // Re-run every lead that dead-lettered during a site-lookup outage (#539\n // Phase 0) through the normal ingest pipeline. Exit 1 while any row is STILL\n // owed — still failing, re-ingested but unmarked (MED-10b), or undecodable\n // (MED-10c). The operator should not read the run as \"all leads landed\".\n // Zero rows is a clean 0: nothing owed.\n if (action === \"replay-deadletters\") {\n const { readDbConfig, openDb } = await import(\"../../db/client.js\");\n // #786. `--abandon` is the escape hatch for the queue #785 deliberately\n // stopped draining: a slug that is genuinely dead but still deployed keeps\n // dead-lettering leads, holds this command at exit 1, and leaves a standing\n // CRITICAL cockpit item. Validated BEFORE opening any store, so a missing\n // reason refuses without needing Turso creds.\n if (opts.abandon !== undefined) {\n const reason = (opts.reason ?? \"\").trim();\n if (reason === \"\") {\n return {\n output:\n 'db replay-deadletters --abandon refused: pass --reason \"…\". Abandoning writes ' +\n \"off a client's captured leads, and the record of WHY has to outlive the decision.\",\n code: 1,\n };\n }\n const by = opts.by ?? process.env.OPERATOR_EMAIL ?? \"operator\";\n const db = await openDb(opts.url ? { url: opts.url } : readDbConfig());\n const { abandonDeadLetters } = await import(\"../../db/deadletter.js\");\n // Row ids are minted `dl_…` (newDeadLetterId), and a site slug never is —\n // so the one argument can name either without a second flag.\n const target = opts.abandon.startsWith(\"dl_\") ? { id: opts.abandon } : { slug: opts.abandon };\n const ids = await abandonDeadLetters(db, { ...target, by, reason, now: new Date() });\n const lines = [\n ...ids.map((id) => `abandoned ${id}`),\n `DEADLETTER_ABANDONED target=${opts.abandon} rows=${ids.length} by=${by}`,\n ];\n return { output: lines.join(\"\\n\"), code: 0 };\n }\n const db = await openDb(opts.url ? { url: opts.url } : readDbConfig());\n const { openBase, readAirtableConfig } = await import(\"../../reports/airtable/client.js\");\n const { getWebsiteBySlug } = await import(\"../../reports/airtable/websites.js\");\n const { getSiteBySlug } = await import(\"../../db/fleet-state.js\");\n const { makeLazySiteLookup } = await import(\"../../forms/site-lookup.js\");\n // #645. Recovery now resolves sites through the SAME lookup the live ingest\n // path uses. It used to call the Airtable `getWebsiteBySlug` directly, so\n // post-#643 the two disagreed about what the fleet is: a site created since\n // the freeze was invisible to the replay, and a row only Airtable still held\n // would have attached a recovered lead to a site the system no longer\n // believes in. `openBase` is passed UNCALLED — under the freeze no Airtable\n // credential is read at all, where before a missing PAT refused the whole\n // replay (`readAirtableConfig()` throws) with real leads sitting in the queue.\n const lookupSite = makeLazySiteLookup({\n fromDb: (s) => getSiteBySlug(db, s),\n openAirtable: () => openBase(readAirtableConfig()),\n fromAirtable: (base, s) => getWebsiteBySlug(base, s),\n });\n const {\n createSubmission,\n stampNotified,\n stampFanout,\n findRecentDuplicateSubmissions,\n listRecentSubmissionsForEmail,\n markSubmissionsSpamRetro,\n } = await import(\"../../db/submissions.js\");\n const { makeNotify } = await import(\"../../forms/notify.js\");\n const { classifySpam } = await import(\"../../forms/spam-classifier.js\");\n const { forwardNewsletterToWebhook } = await import(\"../../forms/webhook.js\");\n const { addMailchimpMember, mailchimpTagsFor } = await import(\"../../forms/mailchimp.js\");\n const { defaultResendClient } = await import(\"../../reports/send/resend.js\");\n const { replayDeadLetters } = await import(\"../../forms/replay.js\");\n\n // Same degradation as the ingest handler: an unconfigured Resend key means\n // replayed leads land un-emailed (notify=failed) rather than blocking replay.\n let send = null;\n try {\n send = defaultResendClient().send;\n } catch (err) {\n console.error(`[db] Resend unconfigured; replaying without email: ${String(err)}`);\n }\n\n // Mirrors the production handler's deps minus `deadLetter` (replayDeadLetters\n // forbids and strips it — a throwing lookup must retry, not duplicate) and\n // minus `defer` (a CLI has no post-response phase; the inline tail is fine).\n const result = await replayDeadLetters(db, {\n getWebsiteBySlug: lookupSite,\n createSubmission: (input) => createSubmission(db, input),\n notify: makeNotify(send),\n stampNotified: (id, status, messageId) => stampNotified(db, id, status, messageId),\n now: () => new Date(),\n classifySpam: (n, outcome) =>\n classifySpam({\n name: n.name,\n email: n.email,\n ...(n.message !== undefined ? { message: n.message } : {}),\n formType: n.formType,\n extraFields: n.extraFields,\n turnstile: outcome,\n }),\n findRecentDuplicates: (message, since) =>\n findRecentDuplicateSubmissions(db, message, since.toISOString()),\n listRecentSubmissionsForEmail: (email, since) =>\n listRecentSubmissionsForEmail(db, email, since.toISOString()),\n retroBucket: (ids, reason) => markSubmissionsSpamRetro(db, ids, reason),\n forwardNewsletter: (url, submission, site) =>\n forwardNewsletterToWebhook(url, submission, site),\n addToMailchimp: (site, submission) =>\n addMailchimpMember({\n apiKey: site.mailchimpApiKey ?? \"\",\n audienceId: site.mailchimpAudienceId ?? \"\",\n email: submission.email,\n name: submission.name,\n tags: mailchimpTagsFor(submission.formType),\n }),\n stampFanout: (id, fanoutStatus) => stampFanout(db, id, fanoutStatus),\n });\n\n // MED-10. Four buckets, three of them non-zero-is-not-nothing. `still_failing`\n // is a STANDING condition (a slug awaiting `ensure-site`, or a dead one\n // awaiting `--abandon`) and already has an alarm — the `deadletter` attention\n // item. `unmarked` and `unreadable` are DEFECTS with no other surface at all:\n // this output is the only place either is ever named.\n const lines = [\n ...result.replayed.map(\n (r) => `replayed ${r.id} → ${r.outcome}${r.submissionId ? ` (${r.submissionId})` : \"\"}`,\n ),\n ...result.stillFailing.map((r) => `still failing ${r.id}: ${r.error}`),\n ...result.unmarked.map(\n (r) =>\n `UNMARKED ${r.id}: re-ingested as ${r.submissionId ?? \"(no submission)\"} → ${r.outcome}, ` +\n `but the terminal mark could NOT be written (${r.error}). The row is still queued, so ` +\n `replaying again before it is reconciled will mint a DUPLICATE of this lead.`,\n ),\n ...result.unreadable.map(\n (r) =>\n `UNREADABLE ${r.id} (site '${r.siteSlug}', received ${r.receivedAt}): ${r.error}. ` +\n `The row is untouched — repair the stored JSON, or retire it with ` +\n `\\`db replay-deadletters --abandon ${r.id} --reason \"…\"\\`.`,\n ),\n `DEADLETTER_REPLAY replayed=${result.replayed.length} still_failing=${result.stillFailing.length} unmarked=${result.unmarked.length} unreadable=${result.unreadable.length}`,\n ];\n const owed = result.stillFailing.length + result.unmarked.length + result.unreadable.length > 0;\n return { output: lines.join(\"\\n\"), code: owed ? 1 : 0 };\n }\n\n // Phase 1.3/1.4 of #539. Both read the same two Airtable tables raw (id +\n // fields, no mapRow coercion — the importer's mapping is the authority) and\n // share that mapping, so parity is definitionally checked against what the\n // importer writes.\n if (action === \"import-airtable\" || action === \"parity\" || action === \"sync\") {\n const { TURSO_IS_AUTHORITATIVE } = await import(\"../../db/freeze.js\");\n const refused = freezeGuardsDbWrite(action, opts.force === true, TURSO_IS_AUTHORITATIVE);\n if (refused) return refused;\n const { readDbConfig, openDb } = await import(\"../../db/client.js\");\n const db = await openDb(opts.url ? { url: opts.url } : readDbConfig());\n const { openBase, readAirtableConfig } = await import(\"../../reports/airtable/client.js\");\n const base = openBase(readAirtableConfig());\n const listRaw = async (table: string) =>\n (await base(table).select().all()).map((r) => ({\n id: r.id,\n fields: r.fields as Record<string, unknown>,\n }));\n const io = {\n listWebsiteRecords: () => listRaw(\"Websites\"),\n listReportRecords: () => listRaw(\"Reports\"),\n now: () => new Date(),\n };\n\n if (action === \"import-airtable\") {\n const { importFleetState, formatReapSummary } = await import(\"../../db/import-airtable.js\");\n const summary = await importFleetState(db, {\n ...io,\n // Attachment bodies ride expiring signed URLs; a failed fetch imports the\n // row with rendered_html null and is NAMED in the summary, never silent.\n fetchAttachment: async (url) => {\n try {\n const res = await fetch(url);\n return res.ok ? await res.text() : null;\n } catch {\n return null;\n }\n },\n });\n const lines = [\n `imported ${summary.sites} site(s) → sites/site_health/site_schedule`,\n `imported ${summary.reports} report(s)`,\n ];\n if (summary.renderedHtmlMisses.length > 0) {\n lines.push(\n `⚠ ${summary.renderedHtmlMisses.length} report(s) imported WITHOUT Rendered HTML ` +\n `(fetch failed / URL expired): ${summary.renderedHtmlMisses.join(\", \")}`,\n );\n }\n // The import deletes rows Airtable no longer has, so this one-shot path\n // reports the reap exactly as `db sync` does — same formatter, no second\n // copy to fall out of step.\n lines.push(...formatReapSummary(summary.reaped));\n return { output: lines.join(\"\\n\"), code: 0 };\n }\n\n // Phase 2 backbone (#539): one hourly pass = import (attachment fetches\n // only where the stored row lacks a body) + parity + one retry to absorb\n // the import-read/parity-read race. Exit 1 on persistent mismatch.\n if (action === \"sync\") {\n const { syncFleetState, formatSyncResult } = await import(\"../../db/sync.js\");\n const result = await syncFleetState(db, {\n ...io,\n fetchAttachment: async (url) => {\n try {\n const res = await fetch(url);\n return res.ok ? await res.text() : null;\n } catch {\n return null;\n }\n },\n });\n return {\n output: formatSyncResult(result),\n code: result.parity.mismatches.length > 0 ? 1 : 0,\n };\n }\n\n const { checkFleetParity, formatParityResult } = await import(\"../../db/parity.js\");\n const result = await checkFleetParity(db, io);\n return { output: formatParityResult(result), code: result.mismatches.length > 0 ? 1 : 0 };\n }\n\n // One-shot completion of design D5 (#539 Phase 2): copy every site's CURRENT\n // Airtable \"Header image\" attachment into sites.header_image*. Idempotent —\n // an already-populated BLOB is never overwritten (a re-run must not clobber\n // a freshly generated image with a stale Airtable copy). Exit 1 when any\n // fetch failed, so a partial backfill is never read as complete.\n if (action === \"backfill-header-images\") {\n const { readDbConfig, openDb } = await import(\"../../db/client.js\");\n const db = await openDb(opts.url ? { url: opts.url } : readDbConfig());\n const { openBase, readAirtableConfig } = await import(\"../../reports/airtable/client.js\");\n const base = openBase(readAirtableConfig());\n const { backfillHeaderImages, formatBackfillResult } =\n await import(\"../../db/header-images.js\");\n const result = await backfillHeaderImages(db, {\n listWebsiteRecords: async () =>\n (await base(\"Websites\").select().all()).map((r) => ({\n id: r.id,\n fields: r.fields as Record<string, unknown>,\n })),\n fetchBytes: async (url) => {\n try {\n const res = await fetch(url);\n return res.ok ? new Uint8Array(await res.arrayBuffer()) : null;\n } catch {\n return null;\n }\n },\n });\n return { output: formatBackfillResult(result), code: result.failed.length > 0 ? 1 : 0 };\n }\n\n // #609: one-shot copy of the single Airtable \"Digest State\" row into Turso,\n // so the first digest run after the read repoint sees yesterday's snapshot\n // instead of an empty one. An empty read is not a crash — it badges EVERY\n // item NEW, which lands in the operator's inbox reading as \"the whole fleet\n // degraded overnight\". REFUSES to overwrite a snapshot Turso already holds:\n // a re-run must never replace a fresher snapshot with a stale Airtable copy.\n if (action === \"backfill-digest-state\") {\n const { readDbConfig, openDb } = await import(\"../../db/client.js\");\n const db = await openDb(opts.url ? { url: opts.url } : readDbConfig());\n const { readDigestState: readTurso, writeDigestState: writeTurso } =\n await import(\"../../db/digest-state.js\");\n const existing = await readTurso(db);\n if (Object.keys(existing).length > 0) {\n return {\n output: `DIGEST_BACKFILL skipped=1 reason=turso-already-populated keys=${Object.keys(existing).length}`,\n code: 0,\n };\n }\n const { openBase, readAirtableConfig } = await import(\"../../reports/airtable/client.js\");\n const base = openBase(readAirtableConfig());\n const { readDigestState: readAirtable, DIGEST_STATE_TABLE } =\n await import(\"../../alerts/digest-state.js\");\n // `source` separates \"Airtable has no row\" from \"Airtable has a row holding\n // an empty snapshot\" — the reader collapses BOTH to {}, so copied=0 alone\n // cannot tell a quiet fleet from a failed read. Learned by running this: the\n // first real run printed copied=0 and only a hand probe showed the row was\n // there and genuinely empty.\n const rows = await base(DIGEST_STATE_TABLE).select({ maxRecords: 1, pageSize: 1 }).all();\n const snap = await readAirtable(base);\n const keys = Object.keys(snap).length;\n await writeTurso(db, snap);\n // Read it BACK. A returning write is not evidence the row landed — the same\n // rule forms-notify-target learned on 2026-08-03. `stored` counts the ROW,\n // not its keys, so an empty-but-present snapshot verifies as written.\n const stored = (await db.selectFrom(\"digest_state\").selectAll().execute()).length;\n const after = Object.keys(await readTurso(db)).length;\n return {\n output:\n `DIGEST_BACKFILL source=${rows.length > 0 ? \"row\" : \"absent\"} ` +\n `copied=${keys} verified=${after} rows=${stored}`,\n code: after === keys && stored === 1 ? 0 : 1,\n };\n }\n\n // Phase 1.5 of #539: platform-auth-free SQL dump to stdout-adjacent output.\n // The nightly backup workflow redirects this to a file, encrypts, uploads;\n // the rehearsed restore loads it into stock sqlite3 and compares row counts.\n if (action === \"dump\") {\n const { readDbConfig } = await import(\"../../db/client.js\");\n const cfg = opts.url ? { url: opts.url } : readDbConfig();\n const { createClient } = await import(\"@libsql/client\");\n const client = createClient(cfg.url === \":memory:\" ? { url: \":memory:\" } : cfg);\n const { dumpDatabase } = await import(\"../../db/dump.js\");\n const sql = await dumpDatabase(\n {\n execute: async (q) => {\n const r = await client.execute(q);\n return { columns: r.columns, rows: r.rows as Array<Record<string, unknown>> };\n },\n },\n new Date().toISOString(),\n );\n return { output: sql, code: 0 };\n }\n\n // Load a dump back into a REAL libSQL target (#612 review). The nightly\n // rehearsal loads into `:memory:`, which proves the SQL parses — it does not\n // prove you can get the data back into Turso, and that is the operation an\n // actual recovery needs. Replaying ~17 MB of SQL with megabytes of inline hex\n // over HTTP is materially different from an in-process load, and it had never\n // been done. Refuses to touch a database that already holds rows: a restore\n // is for an EMPTY target, and pointing this at production by mistake should\n // cost nothing.\n if (action === \"restore\") {\n const file = opts.file;\n if (!file) return { output: \"restore: pass the dump path via --file\", code: 1 };\n if (!opts.url) {\n return {\n output:\n \"restore: pass the TARGET database via --url (never defaults, to keep production out of reach)\",\n code: 1,\n };\n }\n // Classify the target BEFORE reading the dump, so a missing token names\n // itself instead of arriving as an opaque 401 (or, worse, as an ENOENT that\n // sends you hunting for the dump file). This command built its client from\n // a url alone until 2026-08-26, which worked against every target the tests\n // and rehearsals used — `:memory:` and a local `turso dev` — and failed\n // against every target an actual recovery has.\n const { parseDumpManifest, requiresAuthToken } = await import(\"../../db/dump.js\");\n const authToken = deps.restoreAuthToken ?? process.env.TURSO_RESTORE_AUTH_TOKEN ?? \"\";\n if (requiresAuthToken(opts.url) && !authToken) {\n return { output: \"RESTORE refused=auth-token-absent\", code: 1 };\n }\n const { readFile } = await import(\"node:fs/promises\");\n const sql = await readFile(file, \"utf-8\");\n const manifest = parseDumpManifest(sql);\n if (!manifest) return { output: \"RESTORE refused=manifest-absent\", code: 1 };\n const { createClient } = await import(\"@libsql/client\");\n const target = createClient(authToken ? { url: opts.url, authToken } : { url: opts.url });\n const existing = await target.execute(\n \"SELECT COUNT(*) AS n FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'\",\n );\n if (Number(existing.rows[0]?.n ?? 0) > 0) {\n return { output: \"RESTORE refused=target-not-empty\", code: 1 };\n }\n await target.executeMultiple(sql);\n const { tableCounts, headerImageBytes } = await import(\"../../db/dump.js\");\n const exec = {\n execute: async (q: string) => {\n const r = await target.execute(q);\n return { columns: r.columns, rows: r.rows as Array<Record<string, unknown>> };\n },\n };\n const counts = await tableCounts(exec);\n const bytes = await headerImageBytes(exec);\n // Same origin-anchored comparison as verify-dump: a restore that \"succeeded\"\n // with fewer rows than the origin held is not a restore.\n const bad: string[] = [];\n for (const [t, want] of Object.entries(manifest.tables)) {\n if ((counts[t] ?? 0) !== want) bad.push(`${t}: origin=${want} restored=${counts[t] ?? 0}`);\n }\n if (bytes !== manifest.blobBytes)\n bad.push(`header_image bytes: origin=${manifest.blobBytes} restored=${bytes}`);\n const rows = Object.values(counts).reduce((a, b) => a + b, 0);\n return {\n output: [\n ...bad.map((b) => `✗ ${b}`),\n `RESTORE loaded=true tables=${Object.keys(counts).length} rows=${rows} blob_bytes=${bytes} mismatches=${bad.length}`,\n ].join(\"\\n\"),\n code: bad.length > 0 ? 1 : 0,\n };\n }\n\n // How much of the plan's quota the fleet has burned this billing cycle\n // (#539 HIGH-10). The starter plan carries `overages: false`, so crossing a\n // quota BLOCKS reads and writes rather than billing for them — and once the\n // Airtable cutover lands, Turso is the only store there is. This is the one\n // alarm that fires before a wall rather than after it.\n //\n // Needs a PLATFORM token, which is a different credential from the\n // database-level TURSO_AUTH_TOKEN the rest of the fleet runs on: the database\n // token cannot read quota state at all.\n if (action === \"usage\") {\n const token = deps.platformToken ?? process.env.TURSO_FLEET_USAGE ?? \"\";\n // An unconfigured alarm must not read as a quiet, healthy one. The tell for\n // \"never ran\" has to be a failure, not a missing line (#585).\n if (!token) {\n return {\n output:\n \"FLEET_DB_USAGE verdict=no-token — set TURSO_FLEET_USAGE (turso auth api-tokens mint …). \" +\n \"This is the PLATFORM token, not the database TURSO_AUTH_TOKEN.\",\n code: 1,\n };\n }\n const { collectUsage, assessUsage } = await import(\"../../db/usage.js\");\n const input = await collectUsage({\n token,\n org: opts.org ?? process.env.TURSO_ORG,\n fetchImpl: deps.fetchImpl,\n now: deps.now ?? new Date(),\n });\n const r = assessUsage(input);\n const window = `${input.cycleStart.toISOString().slice(0, 10)} → ${input.cycleEnd\n .toISOString()\n .slice(0, 10)}`;\n return {\n output: [`Turso plan=${input.plan} billing cycle ${window}`, ...r.lines, \"\", r.marker].join(\n \"\\n\",\n ),\n code: r.code,\n };\n }\n\n // The restore rehearsal (Phase 1.5's hard gate), runnable every night: load\n // the dump into a FRESH in-memory engine and compare what came back against\n // the ORIGIN MANIFEST the dump carries.\n //\n // It used to compare against INSERT counts parsed out of the dump text — i.e.\n // the dump against itself. Both sides derived from one artifact, so a dump\n // that collected 5 of 44 sites shrank both numbers together and verified\n // clean. The manifest is read from the live database before any row is\n // serialised, which is the only measurement that can notice a short dump.\n //\n // A dump that cannot restore is not a backup — and per the repo's instrument\n // rule the check emits its machine line on every run, clean included.\n if (action === \"verify-dump\") {\n const file = opts.file;\n if (!file) return { output: \"verify-dump: pass the dump path via --file\", code: 1 };\n const { readFile } = await import(\"node:fs/promises\");\n const sql = await readFile(file, \"utf-8\");\n const { createClient } = await import(\"@libsql/client\");\n const scratch = createClient({ url: \":memory:\" });\n try {\n await scratch.executeMultiple(sql);\n } catch (err) {\n return { output: `DUMP_VERIFY loaded=false error=${String(err)}`, code: 1 };\n }\n const { tableCounts, headerImageBytes, parseDumpManifest } = await import(\"../../db/dump.js\");\n const manifest = parseDumpManifest(sql);\n if (!manifest) {\n // Refuse rather than fall back to self-comparison. Falling back would\n // re-enable exactly the blind spot the manifest exists to close, and it\n // would do so silently on the one artifact nobody was watching.\n return {\n output: \"DUMP_VERIFY loaded=true manifest=absent — dump predates the origin manifest\",\n code: 1,\n };\n }\n const exec = {\n execute: async (q: string) => {\n const r = await scratch.execute(q);\n return { columns: r.columns, rows: r.rows as Array<Record<string, unknown>> };\n },\n };\n const restored = await tableCounts(exec);\n const restoredBlobBytes = await headerImageBytes(exec);\n const mismatches: string[] = [];\n for (const [table, want] of Object.entries(manifest.tables)) {\n if ((restored[table] ?? 0) !== want) {\n mismatches.push(`${table}: origin=${want} restored=${restored[table] ?? 0}`);\n }\n }\n // A table the origin held and the dump never mentioned would otherwise be\n // invisible: absent from `restored` AND absent from the loop above.\n for (const table of Object.keys(restored)) {\n if (!(table in manifest.tables)) mismatches.push(`${table}: not in origin manifest`);\n }\n // Coverage: every table the APP owns must be present. `tables=N` used to be\n // printed and never asserted, so a table a migration failed to create — or\n // that the dump lost — rode green forever. `digest_state` and\n // `prospect_audits` were both absent from every artifact the night this was\n // found, purely because they postdated the last run.\n const { DATABASE_TABLES } = await import(\"../../db/schema.js\");\n for (const table of DATABASE_TABLES) {\n if (!(table in restored)) mismatches.push(`${table}: MISSING from the backup entirely`);\n }\n if (restoredBlobBytes !== manifest.blobBytes) {\n mismatches.push(\n `header_image bytes: origin=${manifest.blobBytes} restored=${restoredBlobBytes}`,\n );\n }\n const total = Object.values(restored).reduce((a, b) => a + b, 0);\n const lines = [\n ...mismatches.map((m) => `✗ ${m}`),\n `DUMP_VERIFY loaded=true tables=${Object.keys(restored).length} rows=${total} blob_bytes=${restoredBlobBytes} mismatches=${mismatches.length}`,\n ];\n return { output: lines.join(\"\\n\"), code: mismatches.length > 0 ? 1 : 0 };\n }\n\n return {\n output: `unknown db action '${action}'. Use: migrate, replay-deadletters, import-airtable, parity, sync, backfill-header-images, backfill-digest-state, dump, verify-dump, restore.`,\n code: 1,\n };\n}\n"],"mappings":"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","names":["db","lines","result"]}
@@ -1,17 +1,21 @@
1
1
  import {
2
2
  abandonDeadLetters,
3
3
  countUnreplayedDeadLettersBySlug,
4
+ countUnreplayedDeadLettersForSlug,
4
5
  createDeadLetter,
5
6
  listUnreplayedDeadLetters,
6
7
  markDeadLetterReplayed,
7
- newDeadLetterId
8
- } from "./chunk-A5QKNOXW.js";
8
+ newDeadLetterId,
9
+ readUnreplayedDeadLetters
10
+ } from "./chunk-TTT6HYPW.js";
9
11
  export {
10
12
  abandonDeadLetters,
11
13
  countUnreplayedDeadLettersBySlug,
14
+ countUnreplayedDeadLettersForSlug,
12
15
  createDeadLetter,
13
16
  listUnreplayedDeadLetters,
14
17
  markDeadLetterReplayed,
15
- newDeadLetterId
18
+ newDeadLetterId,
19
+ readUnreplayedDeadLetters
16
20
  };
17
- //# sourceMappingURL=deadletter-G4M3HSVM.js.map
21
+ //# sourceMappingURL=deadletter-A7RQVNG3.js.map
@@ -14,12 +14,12 @@ import {
14
14
  collectRenovateAlerts,
15
15
  collectTurnstileGuardrailAlerts,
16
16
  collectVulnAlerts
17
- } from "./chunk-PARKXDXI.js";
18
- import "./chunk-VMGHQUTV.js";
17
+ } from "./chunk-ORTOMEYR.js";
18
+ import "./chunk-B2BRFWJY.js";
19
19
  import "./chunk-PZR4EBPY.js";
20
20
  import {
21
21
  isIdempotencyConflict
22
- } from "./chunk-LDHRIQUW.js";
22
+ } from "./chunk-ZEE5WQYU.js";
23
23
  import {
24
24
  listAllReports
25
25
  } from "./chunk-TEMQPSCM.js";
@@ -51,9 +51,9 @@ import "./chunk-RQJFGK3A.js";
51
51
  import {
52
52
  defaultResendClient
53
53
  } from "./chunk-WEKL2HYJ.js";
54
- import "./chunk-YXWBM26P.js";
55
- import "./chunk-MMSMZC7S.js";
56
- import "./chunk-IOTOOTII.js";
54
+ import "./chunk-3YALLSBF.js";
55
+ import "./chunk-MNRWHSFS.js";
56
+ import "./chunk-CP6MVZ5X.js";
57
57
  import "./chunk-RARREDQE.js";
58
58
  import "./chunk-6VAL7XJT.js";
59
59
 
@@ -153,9 +153,9 @@ async function listPendingApproval(base) {
153
153
  async function fetchNotifyBounceCounts(now) {
154
154
  try {
155
155
  const [{ openDb, readDbConfig }, { countNotifyBouncedBySite }, { screenOutsSince }] = await Promise.all([
156
- import("./client-DGTO65WB.js"),
157
- import("./submissions-JDZP35JM.js"),
158
- import("./screenouts-7P5GOZMV.js")
156
+ import("./client-3XGSZMLU.js"),
157
+ import("./submissions-4I66W6QK.js"),
158
+ import("./screenouts-7NQUWHOJ.js")
159
159
  ]);
160
160
  const db = await openDb(readDbConfig());
161
161
  return await countNotifyBouncedBySite(db, screenOutsSince(now, NOTIFY_BOUNCE_WINDOW_DAYS));
@@ -167,8 +167,8 @@ async function fetchNotifyBounceCounts(now) {
167
167
  async function fetchDeadLetterCounts() {
168
168
  try {
169
169
  const [{ openDb, readDbConfig }, { countUnreplayedDeadLettersBySlug }] = await Promise.all([
170
- import("./client-DGTO65WB.js"),
171
- import("./deadletter-G4M3HSVM.js")
170
+ import("./client-3XGSZMLU.js"),
171
+ import("./deadletter-A7RQVNG3.js")
172
172
  ]);
173
173
  const db = await openDb(readDbConfig());
174
174
  return await countUnreplayedDeadLettersBySlug(db);
@@ -179,7 +179,7 @@ async function fetchDeadLetterCounts() {
179
179
  }
180
180
  async function readDigestStateFromDb() {
181
181
  const [{ openDb, readDbConfig }, { readDigestState }] = await Promise.all([
182
- import("./client-DGTO65WB.js"),
182
+ import("./client-3XGSZMLU.js"),
183
183
  import("./digest-state-ZDPKR7MW.js")
184
184
  ]);
185
185
  return readDigestState(await openDb(readDbConfig()));
@@ -193,7 +193,7 @@ async function writeRollupOnce(options, now) {
193
193
  }
194
194
  async function writeDigestStateToDb(next) {
195
195
  const [{ openDb, readDbConfig }, { writeDigestState: writeDigestState2 }] = await Promise.all([
196
- import("./client-DGTO65WB.js"),
196
+ import("./client-3XGSZMLU.js"),
197
197
  import("./digest-state-ZDPKR7MW.js")
198
198
  ]);
199
199
  await writeDigestState2(await openDb(readDbConfig()), next);
@@ -201,11 +201,11 @@ async function writeDigestStateToDb(next) {
201
201
  async function writeCockpitRollupToDb(now) {
202
202
  if (!process.env.TURSO_DATABASE_URL) return "absent";
203
203
  const [{ openDb, readDbConfig }, { writeCockpitRollup }, screenouts, submissions, collectors] = await Promise.all([
204
- import("./client-DGTO65WB.js"),
204
+ import("./client-3XGSZMLU.js"),
205
205
  import("./digest-state-ZDPKR7MW.js"),
206
- import("./screenouts-7P5GOZMV.js"),
207
- import("./submissions-JDZP35JM.js"),
208
- import("./digest-collectors-CYTN2EUX.js")
206
+ import("./screenouts-7NQUWHOJ.js"),
207
+ import("./submissions-4I66W6QK.js"),
208
+ import("./digest-collectors-CX2NCGS7.js")
209
209
  ]);
210
210
  const db = await openDb(readDbConfig());
211
211
  const SCREEN_OUT_WINDOW_DAYS = 30;
@@ -259,8 +259,8 @@ var SUBMISSIONS_DIGEST_WINDOW_MS = 24 * 60 * 60 * 1e3;
259
259
  async function fetchSubmissionsDigestCounts(now) {
260
260
  try {
261
261
  const [{ openDb, readDbConfig }, { countSubmissionsSinceBySite }] = await Promise.all([
262
- import("./client-DGTO65WB.js"),
263
- import("./submissions-JDZP35JM.js")
262
+ import("./client-3XGSZMLU.js"),
263
+ import("./submissions-4I66W6QK.js")
264
264
  ]);
265
265
  const db = await openDb(readDbConfig());
266
266
  return await countSubmissionsSinceBySite(
@@ -448,4 +448,4 @@ export {
448
448
  renderDigestHtml,
449
449
  runDigest
450
450
  };
451
- //# sourceMappingURL=digest-CWKK2DMR.js.map
451
+ //# sourceMappingURL=digest-AMXRGWZA.js.map
@@ -13,10 +13,10 @@ import {
13
13
  collectTurnstileGuardrailAlerts,
14
14
  collectVulnAlerts,
15
15
  prismicAckIsLive
16
- } from "./chunk-PARKXDXI.js";
17
- import "./chunk-VMGHQUTV.js";
16
+ } from "./chunk-ORTOMEYR.js";
17
+ import "./chunk-B2BRFWJY.js";
18
18
  import "./chunk-PZR4EBPY.js";
19
- import "./chunk-LDHRIQUW.js";
19
+ import "./chunk-ZEE5WQYU.js";
20
20
  import "./chunk-TEMQPSCM.js";
21
21
  import "./chunk-ASLDCWBF.js";
22
22
  import "./chunk-VAD43MOR.js";
@@ -31,9 +31,9 @@ import "./chunk-TC4GANMX.js";
31
31
  import "./chunk-ICSRWW5Q.js";
32
32
  import "./chunk-RQJFGK3A.js";
33
33
  import "./chunk-WEKL2HYJ.js";
34
- import "./chunk-YXWBM26P.js";
35
- import "./chunk-MMSMZC7S.js";
36
- import "./chunk-IOTOOTII.js";
34
+ import "./chunk-3YALLSBF.js";
35
+ import "./chunk-MNRWHSFS.js";
36
+ import "./chunk-CP6MVZ5X.js";
37
37
  import "./chunk-RARREDQE.js";
38
38
  import "./chunk-6VAL7XJT.js";
39
39
  export {
@@ -52,4 +52,4 @@ export {
52
52
  collectVulnAlerts,
53
53
  prismicAckIsLive
54
54
  };
55
- //# sourceMappingURL=digest-collectors-CYTN2EUX.js.map
55
+ //# sourceMappingURL=digest-collectors-CX2NCGS7.js.map
@@ -30,7 +30,7 @@ async function runEnsureSiteCommand(slug, opts, deps = {}) {
30
30
  if (!slug) return { output: "Provide a <slug> (e.g. `ensure-site roalson`).", code: 2 };
31
31
  try {
32
32
  const open = deps.openDb ?? (async () => {
33
- const { openDb, readDbConfig } = await import("./client-DGTO65WB.js");
33
+ const { openDb, readDbConfig } = await import("./client-3XGSZMLU.js");
34
34
  return openDb(readDbConfig());
35
35
  });
36
36
  const db = await open();
@@ -67,4 +67,4 @@ async function runEnsureSiteCommand(slug, opts, deps = {}) {
67
67
  export {
68
68
  runEnsureSiteCommand
69
69
  };
70
- //# sourceMappingURL=ensure-site-2QZHFVQ3.js.map
70
+ //# sourceMappingURL=ensure-site-F2WFCWWA.js.map
@@ -146,8 +146,8 @@ async function runFormsNotifyTargetCommand(site, opts) {
146
146
  return { output: `--set must be 'on' or 'off' (got '${opts.set}')`, code: 2 };
147
147
  }
148
148
  try {
149
- const { makeSiteMirror } = await import("./site-mirror-WR7ACXOV.js");
150
- const { readFleetRoster } = await import("./roster-GDNSIRAK.js");
149
+ const { makeSiteMirror } = await import("./site-mirror-TKTDWYP3.js");
150
+ const { readFleetRoster } = await import("./roster-EJ3ILR52.js");
151
151
  const result = await formsNotifyTarget({
152
152
  roster: () => readFleetRoster(),
153
153
  siteMirror: await makeSiteMirror(),
@@ -168,4 +168,4 @@ export {
168
168
  formatNotifyTarget,
169
169
  runFormsNotifyTargetCommand
170
170
  };
171
- //# sourceMappingURL=forms-notify-target-X6UEOI2M.js.map
171
+ //# sourceMappingURL=forms-notify-target-DK5R326N.js.map
@@ -26,10 +26,10 @@ import {
26
26
  } from "./chunk-43SW3FGW.js";
27
27
  import {
28
28
  recordFleetEventsBestEffort
29
- } from "./chunk-YXWBM26P.js";
30
- import "./chunk-MMSMZC7S.js";
29
+ } from "./chunk-3YALLSBF.js";
30
+ import "./chunk-MNRWHSFS.js";
31
31
  import "./chunk-LBYOLBW7.js";
32
- import "./chunk-IOTOOTII.js";
32
+ import "./chunk-CP6MVZ5X.js";
33
33
  import "./chunk-6VAL7XJT.js";
34
34
 
35
35
  // src/audits/github-signals.ts
@@ -75,7 +75,7 @@ async function runGitHubSignalsCommand(opts, deps = {}) {
75
75
  }
76
76
  const base = deps.openBase ? deps.openBase() : openBase(readAirtableConfig());
77
77
  const websites = await (deps.roster ?? (async () => {
78
- const { readFleetRoster } = await import("./roster-GDNSIRAK.js");
78
+ const { readFleetRoster } = await import("./roster-EJ3ILR52.js");
79
79
  return readFleetRoster();
80
80
  }))();
81
81
  const gh = deps.makeGh ? deps.makeGh(token) : makeGitHub({ token });
@@ -96,7 +96,7 @@ async function runGitHubSignalsCommand(opts, deps = {}) {
96
96
  );
97
97
  const sweptAt = (/* @__PURE__ */ new Date()).toISOString();
98
98
  const makeMirror = deps.makeMirror ?? (async () => {
99
- const { makeHealthMirrorBestEffort } = await import("./health-mirror-KU3G7V2Y.js");
99
+ const { makeHealthMirrorBestEffort } = await import("./health-mirror-WZUHWCCV.js");
100
100
  return makeHealthMirrorBestEffort();
101
101
  });
102
102
  const mirror = await makeMirror();
@@ -161,4 +161,4 @@ export {
161
161
  githubSignalsExitCode,
162
162
  runGitHubSignalsCommand
163
163
  };
164
- //# sourceMappingURL=github-signals-QIRTHHET.js.map
164
+ //# sourceMappingURL=github-signals-RGRPYTUB.js.map
@@ -88,7 +88,7 @@ async function generateForTargets(targets, opts, generate = generateHeaderImage)
88
88
  return { output: lines.join("\n"), code: failed > 0 ? 1 : 0 };
89
89
  }
90
90
  async function runHeaderImageCommand(site, opts) {
91
- const { readFleetRoster } = await import("./roster-GDNSIRAK.js");
91
+ const { readFleetRoster } = await import("./roster-EJ3ILR52.js");
92
92
  const rows = await readFleetRoster();
93
93
  const targets = resolveTargets(rows, { site, all: opts.all, force: opts.force });
94
94
  if (targets.length === 0) {
@@ -99,7 +99,7 @@ async function runHeaderImageCommand(site, opts) {
99
99
  }
100
100
  let withStore = opts;
101
101
  if (opts.writeBack && !opts.storeDb && process.env.TURSO_DATABASE_URL) {
102
- const { openDb, readDbConfig } = await import("./client-DGTO65WB.js");
102
+ const { openDb, readDbConfig } = await import("./client-3XGSZMLU.js");
103
103
  const { storeHeaderImage } = await import("./header-images-SQBCE5OD.js");
104
104
  const db = await openDb(readDbConfig());
105
105
  withStore = { ...opts, storeDb: (id, img) => storeHeaderImage(db, id, img) };
@@ -112,4 +112,4 @@ export {
112
112
  resolveTargets,
113
113
  runHeaderImageCommand
114
114
  };
115
- //# sourceMappingURL=header-image-H7ZJAV26.js.map
115
+ //# sourceMappingURL=header-image-43ZIEJKP.js.map
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-DYTYPKFN.js";
9
9
  import {
10
10
  resolveSites
11
- } from "./chunk-FP6HOW6J.js";
11
+ } from "./chunk-S2PLX4XK.js";
12
12
  import {
13
13
  healthEndpoint
14
14
  } from "./chunk-LAG6IP7Y.js";
@@ -52,4 +52,4 @@ async function runHealthEndpointCommand(site, opts) {
52
52
  export {
53
53
  runHealthEndpointCommand
54
54
  };
55
- //# sourceMappingURL=health-endpoint-BJ6BKRHF.js.map
55
+ //# sourceMappingURL=health-endpoint-K5LXDPGE.js.map
@@ -12,8 +12,8 @@ import "./chunk-ICSRWW5Q.js";
12
12
  import {
13
13
  openDb,
14
14
  readDbConfig
15
- } from "./chunk-MMSMZC7S.js";
16
- import "./chunk-IOTOOTII.js";
15
+ } from "./chunk-MNRWHSFS.js";
16
+ import "./chunk-CP6MVZ5X.js";
17
17
  import "./chunk-6VAL7XJT.js";
18
18
 
19
19
  // src/audits/health-mirror.ts
@@ -43,4 +43,4 @@ export {
43
43
  makeHealthMirrorBestEffort,
44
44
  makeScheduleMirrorBestEffort
45
45
  };
46
- //# sourceMappingURL=health-mirror-KU3G7V2Y.js.map
46
+ //# sourceMappingURL=health-mirror-WZUHWCCV.js.map
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import "./chunk-DZZ5KRTB.js";
1
2
  import "./chunk-YDZ7XGPZ.js";
2
3
  import {
3
4
  draftReportForSite
@@ -6,7 +7,7 @@ import {
6
7
  DEFAULT_INIT_STEPS,
7
8
  a11yFixturesPage,
8
9
  init
9
- } from "./chunk-U2ZI6G7H.js";
10
+ } from "./chunk-GRF2RYEO.js";
10
11
  import "./chunk-EN6CTBUN.js";
11
12
  import {
12
13
  onboard,
@@ -34,10 +35,10 @@ import {
34
35
  requireFleetWorkdir,
35
36
  selectFleetSites
36
37
  } from "./chunk-VQMAAQ5M.js";
37
- import "./chunk-PARKXDXI.js";
38
+ import "./chunk-ORTOMEYR.js";
38
39
  import {
39
40
  approveBlockers
40
- } from "./chunk-VMGHQUTV.js";
41
+ } from "./chunk-B2BRFWJY.js";
41
42
  import {
42
43
  findDueReports
43
44
  } from "./chunk-PZR4EBPY.js";
@@ -45,7 +46,7 @@ import {
45
46
  parseAddresses,
46
47
  sendApprovedReports,
47
48
  withGlobalCc
48
- } from "./chunk-LDHRIQUW.js";
49
+ } from "./chunk-ZEE5WQYU.js";
49
50
  import "./chunk-TEMQPSCM.js";
50
51
  import "./chunk-BRYXFH77.js";
51
52
  import "./chunk-P7323YVW.js";
@@ -106,7 +107,7 @@ import {
106
107
  runAudits,
107
108
  runAuditsAcross,
108
109
  securityAudit
109
- } from "./chunk-L3S3QZXL.js";
110
+ } from "./chunk-DXZRWW2F.js";
110
111
  import "./chunk-LKGVSM2O.js";
111
112
  import "./chunk-MVKNU4QD.js";
112
113
  import "./chunk-43SW3FGW.js";
@@ -114,15 +115,15 @@ import {
114
115
  fromJsonFile,
115
116
  localPath
116
117
  } from "./chunk-Z3JXLULD.js";
117
- import "./chunk-YXWBM26P.js";
118
- import "./chunk-MMSMZC7S.js";
118
+ import "./chunk-3YALLSBF.js";
119
+ import "./chunk-MNRWHSFS.js";
119
120
  import {
120
121
  lighthouseAudit
121
- } from "./chunk-BIYQN7FH.js";
122
+ } from "./chunk-VCPTOZ56.js";
122
123
  import "./chunk-UDRHAQT7.js";
123
124
  import "./chunk-PTDMVGKL.js";
124
125
  import "./chunk-LBYOLBW7.js";
125
- import "./chunk-IOTOOTII.js";
126
+ import "./chunk-CP6MVZ5X.js";
126
127
  import "./chunk-A3WDWNN5.js";
127
128
  import {
128
129
  isLeadFormType
@@ -1891,8 +1892,6 @@ var SESSION_TTL_SECONDS = 30 * 24 * 60 * 60;
1891
1892
 
1892
1893
  // src/dashboard/prospect-audit-trigger.ts
1893
1894
  var PROSPECT_AUDIT_DUPLICATE_WINDOW_MS = 10 * 60 * 1e3;
1894
- var PROSPECT_AUDIT_DAILY_CAP = 25;
1895
- var DAILY_CAP_LOOKBACK = PROSPECT_AUDIT_DAILY_CAP * 2;
1896
1895
  export {
1897
1896
  ALL_AUDIT_NAMES,
1898
1897
  ALL_RECIPE_NAMES,