Sample Followup Sweep

repo: ~/Projects/sample-followup-sweep · 253 commits · 30 in last 24h, 75 in last 7d · ▁▁▂▅▃▁█

Search the build

253 commits indexed

  1. 3866478 2026-09-26 auto-data-snapshot: 2026-09-26T17:50:54 (1 data files) — data/stamp-ledger.json
  2. 3b74eb7 2026-09-26 auto-data-snapshot: 2026-09-26T17:20:23 (1 data files) — data/stamp-ledger.json
  3. 3922937 2026-09-26 auto-data-snapshot: 2026-09-26T16:49:35 (1 data files) — data/stamp-ledger.json
  4. 24363fb 2026-09-26 auto-data-snapshot: 2026-09-26T16:19:05 (1 data files) — data/stamp-ledger.json
  5. 96007d0 2026-09-26 auto-data-snapshot: 2026-09-26T15:48:33 (1 data files) — data/stamp-ledger.json
  6. 9eef1f6 2026-09-26 auto-data-snapshot: 2026-09-26T15:18:06 (1 data files) — data/stamp-ledger.json
  7. 8ce0936 2026-09-26 auto-data-snapshot: 2026-09-26T14:47:36 (1 data files) — data/stamp-ledger.json
  8. b1007c1 2026-09-26 auto-data-snapshot: 2026-09-26T14:17:06 (1 data files) — data/stamp-ledger.json
  9. 65202c7 2026-09-26 auto-data-snapshot: 2026-09-26T13:46:36 (1 data files) — data/stamp-ledger.json
  10. 4eb4951 2026-09-26 stamp_aliases: drop unverified RBL rebelwalls alias (RS13181 already stamped via gimmersta 09-04; Cody #3), document WQ alias evidence (TK-12351)
  11. d8151a8 2026-09-26 auto-data-snapshot: 2026-09-26T13:16:02 (1 data files) — data/stamp-ledger.json
  12. d7b4973 2026-09-26 stamp_aliases: stamp-only recipient aliases (WQ contract@/lindsaydetwiler@, RBL support@rebelwalls.com); send routing unaffected + test (TK-12351)
  13. 5b98d5b 2026-09-26 Sent-stamp watcher: dead-letter heartbeat + vid-keyed pull-forward (TK-12351)
  14. 60334c7 2026-09-26 5x report: sweep 5 + Cody findings (TK-12320)
  15. 5181a26 2026-09-26 5x sweep 5 (Cody FIX FIRST #2): refuse empty follow-up letters — compose() throws on 0 rows, send-one 409s a 0-item vendor, no send button on empty rows (TK-12320)
  16. 18d9697 2026-09-26 auto-data-snapshot: 2026-09-26T12:44:57 (1 data files) — data/stamp-ledger.json
  17. 0403496 2026-09-26 auto-data-snapshot: 2026-09-26T12:14:18 (1 data files) — data/stamp-ledger.json
  18. 744ed80 2026-09-26 auto-data-snapshot: 2026-09-26T11:43:40 (1 data files) — data/stamp-ledger.json
  19. 1e71744 2026-09-26 auto-data-snapshot: 2026-09-26T11:13:09 (1 data files) — data/stamp-ledger.json
  20. a6df529 2026-09-26 auto-data-snapshot: 2026-09-26T10:42:21 (1 data files) — data/stamp-ledger.json
  21. 02ec413 2026-09-26 auto-data-snapshot: 2026-09-26T10:11:45 (1 data files) — data/stamp-ledger.json
  22. 197cbae 2026-09-26 auto-data-snapshot: 2026-09-26T09:21:27 (1 data files) — data/stamp-ledger.json
  23. f5257c2 2026-09-26 5x report (TK-12320)
  24. a4ab269 2026-09-26 5x sweep 2: console sort/render no longer crash on vendors with 0 outstanding items (TK-12320)
  25. 32af305 2026-09-26 5x sweep 1: npm test runs .cjs suites and fails on any failing file (TK-12320)
  26. d523fb1 2026-09-26 sent-stamp watcher: fold review findings (never overwrite a chase date)
  27. 523a32c 2026-09-26 watch-sent-stamp: refuse unknown args instead of running a live pass (TK-12320)
  28. a884b80 2026-09-26 auto-data-snapshot: 2026-09-26T08:50:16 (1 data files) — data/stamp-ledger.json
  29. a6b53a0 2026-09-26 sent-stamp watcher: retry skipped/zero sends, stamp the real send date
  30. f631b44 2026-09-26 auto-data-snapshot: 2026-09-26T08:19:11 (1 data files) — data/resend-queue.json
  31. c07d434 2026-09-25 auto-data-snapshot: 2026-09-25T13:56:04 (1 data files) — data/stamp-ledger.json
  32. 5843b22 2026-09-25 auto-data-snapshot: 2026-09-25T08:13:10 (1 data files) — data/resend-queue.json
  33. a03c8b7 2026-09-25 auto-data-snapshot: 2026-09-25T07:12:13 (2 data files) — data/outstanding-snapshot.json data/reconciled-draft-ledger.json
  34. b95d293 2026-09-24 auto-data-snapshot: 2026-09-24T12:49:23 (1 data files) — data/stamp-ledger.json
  35. 17e8c13 2026-09-24 auto-data-snapshot: 2026-09-24T12:18:37 (1 data files) — data/stamp-ledger.json
  36. ae3a82b 2026-09-24 auto-data-snapshot: 2026-09-24T09:44:11 (3 data files) — data/coverage-reconcile/latest.json data/draft-ledger.json data/runs/scheduled-09-24-2026.json
  37. 616e3a8 2026-09-24 sample-followup: fix TK-12091 window regression + shared window + canary tripwire
  38. 9f289bf 2026-09-24 auto-data-snapshot: 2026-09-24T09:12:24 (1 data files) — data/coverage-reconcile/latest.json
  39. 83b47fa 2026-09-24 sample-followup: LA Walls/DWLC1059 tracked as known-unresolvable (TK-12119)
  40. b9a4f66 2026-09-24 auto-data-snapshot: 2026-09-24T08:40:54 (2 data files) — data/coverage-reconcile/latest.json data/tk12118-vid-backfill-restore-map.json
  41. 8c3b575 2026-09-24 sample-followup: fix tk12118 SKU guard to verify on REPORT ON SAMPLES ORDERED (TK-12118)
  42. d70e93d 2026-09-24 auto-data-snapshot: 2026-09-24T08:09:22 (3 data files) — data/coverage-reconcile/latest.json data/resend-queue.json data/tk12109-stamp-restore-map.json
  43. 32a76cf 2026-09-24 sample-followup: canary no longer counts deferred recipient-busy rows as covered (TK-12119)
  44. 4b93811 2026-09-24 auto-data-snapshot: 2026-09-24T07:06:26 (2 data files) — data/outstanding-snapshot.json data/reconciled-draft-ledger.json
  45. 367e1e4 2026-09-23 auto-data-snapshot: 2026-09-23T18:30:30 (1 data files) — data/coverage-reconcile/latest.json
  46. 5b5b893 2026-09-23 sample-followup: draft gated vid-backfill for 3 verified leaked SKUs (TK-12118)
  47. b9f7fd7 2026-09-23 auto-data-snapshot: 2026-09-23T16:24:32 (2 data files) — data/contacts.json.bak-20260923-160215-TK-MAJBRAND data/coverage-reconcile/latest.json
  48. 1d2b680 2026-09-23 sample-followup: coverage-reconciliation canary (silent-skip false-green safety net) — TK-12117
  49. 473c034 2026-09-23 sample-followup: resolve MAJ (Majilite) + BRAND (Brand McKenzie) vendor-map holes (TK-12114)
  50. ab598e5 2026-09-23 auto-data-snapshot: 2026-09-23T15:52:37 (3 data files) — data/reconciled-draft-ledger.json.bak-20260923-153620-TK12107 data/runs/scheduled-09-23-2026.json data/stamp-ledger.json
  51. 05177f2 2026-09-23 sample-followup: record 3 manually-sent 10-day letters + gated FM-stamp script (TK-12109)
  52. 6c9b709 2026-09-23 sample-followup: draft the 5 verified-owed 10-day SKUs + CONFIRM-RECIPIENT banner/ledger mark (TK-12107)
  53. d04d106 2026-09-23 sample-followup: two-part never-false-chase guard (arrival-dedup + already-sent) — TK-12105
  54. 0269ad6 2026-09-23 auto-data-snapshot: 2026-09-23T14:48:42 (2 data files) — data/reconciled-draft-ledger.json.bak-20260923-142139-TK12087 data/runs/scheduled-09-23-2026.json
  55. 7534717 2026-09-23 sample-followup: exclude MDCKEN as non-vendor no-chase (TK-12101)
  56. a11a644 2026-09-23 sample-followup: day-of-week-aware window (Tue 4-day catch-up, Wed-Fri strict) — TK-12091
  57. e3a7a73 2026-09-23 sample-followup: single-day rolling window (Entered==today-10), revert >10d backlog (TK-12087)
  58. 04e0986 2026-09-23 auto-data-snapshot: 2026-09-23T14:17:00 (2 data files) — data/outstanding-snapshot.json data/runs/scheduled-09-23-2026.json
  59. 20ca7a9 2026-09-23 auto-data-snapshot: 2026-09-23T13:45:21 (2 data files) — data/contacts.json.bak-20260923-131934-TK12085 scripts/send-reconciled.mjs.bak-20260923-131934-TK12085
  60. 7394a41 2026-09-23 sample-followup: resolve 68 blocked overdue rows (TK-12085)
  61. 0c365b7 2026-09-23 auto-data-snapshot: 2026-09-23T11:38:28 (1 data files) — data/stamp-ledger.json
  62. 23a8301 2026-09-23 auto-data-snapshot: 2026-09-23T11:06:44 (1 data files) — data/stamp-ledger.json
  63. f562332 2026-09-23 auto-data-snapshot: 2026-09-23T08:28:09 (1 data files) — data/resend-queue.json
  64. 176ecba 2026-09-23 auto-data-snapshot: 2026-09-23T07:25:20 (2 data files) — data/outstanding-snapshot.json data/reconciled-draft-ledger.json
  65. 62f2d0f 2026-09-22 auto-data-snapshot: 2026-09-22T15:00:06 (1 data files) — data/stamp-ledger.json
  66. bf91abd 2026-09-22 auto-data-snapshot: 2026-09-22T12:15:47 (1 data files) — data/stamp-ledger.json
  67. 99d3128 2026-09-22 fix(fleet): fall back to contacts.json vendor name before the (vid X) placeholder
  68. 89f8650 2026-09-22 refresh fleet.json from live FM sweep + declare 1838 WALLCOVERINGS slug alias
  69. 14acdb8 2026-09-22 auto-data-snapshot: 2026-09-22T11:11:00 (3 data files) — data/resend-queue.json data/runs/scheduled-09-22-2026.json data/stamp-ledger.json
  70. 5374f08 2026-09-22 fix(window): query full active [today-60..today-10] cohort every run
  71. fcd34dc 2026-09-22 fix(sweep): include exactly-10-day-old requests (age < minAge, was <=)
  72. 685de6e 2026-09-22 auto-data-snapshot: 2026-09-22T07:25:22 (2 data files) — data/outstanding-snapshot.json data/resend-queue.json
  73. afaa0cf 2026-09-21 auto-data-snapshot: 2026-09-21T08:49:26 (1 data files) — data/stamp-ledger.json
  74. 60018de 2026-09-21 auto-data-snapshot: 2026-09-21T08:17:25 (1 data files) — data/resend-queue.json
  75. 6ae2a22 2026-09-20 auto-data-snapshot: 2026-09-20T08:21:12 (1 data files) — data/resend-queue.json
  76. b79d516 2026-09-19 auto-data-snapshot: 2026-09-19T08:23:45 (1 data files) — data/resend-queue.json
  77. 4ff6d05 2026-09-18 auto-data-snapshot: 2026-09-18T08:22:45 (1 data files) — data/resend-queue.json
  78. fb0d872 2026-09-18 auto-data-snapshot: 2026-09-18T07:17:32 (2 data files) — data/outstanding-snapshot.json data/reconciled-draft-ledger.json
  79. a8c84d6 2026-09-17 auto-data-snapshot: 2026-09-17T08:16:42 (1 data files) — data/resend-queue.json
  80. 6fd2357 2026-09-17 auto-data-snapshot: 2026-09-17T07:12:37 (1 data files) — data/outstanding-snapshot.json
  81. 80f93b7 2026-09-16 auto-data-snapshot: 2026-09-16T08:24:08 (2 data files) — data/resend-queue.json data/stamp-ledger.json
  82. dfa0103 2026-09-16 auto-data-snapshot: 2026-09-16T07:20:09 (1 data files) — data/outstanding-snapshot.json
  83. f0ebc1f 2026-09-15 auto-data-snapshot: 2026-09-15T12:00:25 (2 data files) — data/draft-ledger.json data/runs/scheduled-09-15-2026.json
  84. 8cc7756 2026-09-15 Add Barneby Gates (vid=bar) sample contact: studio@+alice@barnebygates.com, acct On File
  85. e471097 2026-09-15 auto-data-snapshot: 2026-09-15T11:25:48 (1 data files) — data/runs/scheduled-09-15-2026.json
  86. 6bd9e4c 2026-09-15 auto-data-snapshot: 2026-09-15T10:20:50 (1 data files) — data/stamp-ledger.json
  87. f203816 2026-09-15 auto-data-snapshot: 2026-09-15T09:48:56 (2 data files) — data/draft-ledger.json data/runs/scheduled-09-15-2026.json
  88. a1ea8e6 2026-09-15 auto-data-snapshot: 2026-09-15T08:12:49 (1 data files) — data/resend-queue.json
  89. 3e1a4cd 2026-09-15 auto-data-snapshot: 2026-09-15T07:09:15 (2 data files) — data/outstanding-snapshot.json data/reconciled-draft-ledger.json
  90. a7dec51 2026-09-14 auto-data-snapshot: 2026-09-14T08:21:43 (1 data files) — data/resend-queue.json
  91. d56b7a2 2026-09-13 auto-data-snapshot: 2026-09-13T08:07:44 (1 data files) — data/resend-queue.json
  92. 924ba34 2026-09-13 auto-data-snapshot: 2026-09-13T00:21:19 (1 data files) — data/stamp-ledger.json
  93. be622a9 2026-09-12 TK-11524: close sample-followup no-email gap (item d)
  94. 319610c 2026-09-12 auto-data-snapshot: 2026-09-12T10:01:53 (2 data files) — data/draft-ledger.json data/runs/scheduled-09-12-2026.json
  95. e97b899 2026-09-12 auto-data-snapshot: 2026-09-12T08:20:59 (5 data files) — data/contacts.json.bak-1789224232 data/contacts.json.bak-1789224483 data/resend-queue.json data/runs/scheduled-09-12-2026.json package-lock.json
  96. 8e1807b 2026-09-12 lib/slug-aliases.cjs: one source of truth for slug routing — the two copies had already diverged (TK-11255)
  97. 5c0d68e 2026-09-12 contacts.json: route VAH + BRU + bare-1838 — 27 of 28 live vendors now reachable (TK-11255)
  98. 6771f15 2026-09-12 scheduled-run: decouple the vid->contact bridge from the fleet snapshot (TK-11255)
  99. a1c761a 2026-09-12 contacts.json: unblock POI + 1838, both silently skipped by the mailer (TK-11255)
  100. e343b5f 2026-09-12 sample-followup: strip CR/whitespace from FileMaker vid — it was silently dropping chases (TK-11255)
  101. eafdc77 2026-09-12 chore: v1.1.0 -> v1.1.1 (TK-11255 vendor-desk routing fix, session close)
  102. e0317eb 2026-09-12 build-fleet: declare slug aliases + fail the build on an undeclared collision (TK-11255)
  103. 47b5b8a 2026-09-11 auto-data-snapshot: 2026-09-11T15:15:48 (2 data files) — data/contacts.json.bak-1789163136 data/contacts.json.bak-1789163794
  104. 706e430 2026-09-11 build-fleet: VID SAND is Sanderson (@ Jasper Showroom), not Sandberg — fix name map so regenerated fleet.json stops mislabeling it (TK-11255)
  105. 42be6f7 2026-09-11 contacts.json: mor (Morris & Co) -> Jasper Showroom desk, matching sand; was departed Nicole.Monbarren@ (TK-11255)
  106. 641d84c 2026-09-11 contacts.json: fix sand — it is Sanderson via Jasper, NOT Sandberg (TK-11255)
  107. c6a49a7 2026-09-11 auto-data-snapshot: 2026-09-11T14:39:38 (3 data files) — data/contacts.json.bak-1789161339 data/contacts.json.bak-1789162028 data/contacts.json.bak-1789162556
  108. f195f74 2026-09-11 contacts.json: fix 3 vendor sample desks on Gmail evidence, not preference (TK-11255)
  109. 6d72c91 2026-09-11 contacts.json: adopt canonical sample desks for dtex + mdc (TK-11255)
  110. f7cb4cb 2026-09-11 contacts.json: fill 7 blank vendor sample desks from canonical FileMaker (TK-11255)
  111. 4bb9f84 2026-09-11 auto-data-snapshot: 2026-09-11T13:23:02 (1 data files) — data/stamp-ledger.json
  112. 65d03c0 2026-09-11 auto-data-snapshot: 2026-09-11T10:08:53 (1 data files) — data/stamp-ledger.json
  113. b0fd0ef 2026-09-11 auto-data-snapshot: 2026-09-11T08:16:47 (1 data files) — data/resend-queue.json
  114. a7c0019 2026-09-11 auto-data-snapshot: 2026-09-11T07:11:09 (2 data files) — data/draft-ledger.json data/runs/scheduled-09-11-2026.json
  115. 843b2df 2026-09-10 auto-data-snapshot: 2026-09-10T13:02:09 (1 data files) — data/runs/scheduled-09-10-2026.json
  116. d0bc2fd 2026-09-10 TK-11409: sent-poller stops pre-filling the 2nd-request slot on a first stamp
  117. 74e43c2 2026-09-10 TK-11409: the static contact list expires instead of blocking forever
  118. 17b5c5c 2026-09-10 auto-data-snapshot: 2026-09-10T12:25:41 (1 data files) — data/runs/scheduled-09-10-2026.json
  119. de5df55 2026-09-10 TK-11409: stop shipping the internal 'From job:' banner to vendors
  120. 42b4eba 2026-09-10 TK-11409: rescue the text of two Aug-15 drafts ~4 days from permanent deletion
  121. b4d9ec1 2026-09-10 TK-11409: anti-dup keys on the CHASE subject, not the new-order subject (DTD 6/7 verdict B)
  122. b128bf8 2026-09-10 auto-data-snapshot: 2026-09-10T11:43:04 (2 data files) — data/draft-ledger.json data/runs/scheduled-09-10-2026.json
  123. ed3bc8d 2026-09-10 TK-11409: verify the draft-ledger against reality; surface both silent draft-mode outcomes
  124. 55c8110 2026-09-10 TK-11409: anti-dup filter moves to the send gate only (DTD 5/5 verdict C)
  125. 54f5ad4 2026-09-10 auto-data-snapshot: 2026-09-10T11:08:25 (2 data files) — data/draft-ledger.json data/runs/scheduled-09-10-2026.json
  126. 1c8e587 2026-09-10 TK-11409: pass 2 requires send-history evidence, and can no longer fail silently
  127. 173735d 2026-09-10 TK-11409: plan --pass 2 previews the resend match set read-only
  128. 0603d8e 2026-09-10 auto-data-snapshot: 2026-09-10T10:34:55 (2 data files) — data/draft-ledger.json data/runs/scheduled-09-10-2026.json
  129. f69c9f7 2026-09-10 TK-11409: the 2nd request now updates the WALLPAPER file
  130. c613b79 2026-09-10 auto-data-snapshot: 2026-09-10T08:12:39 (1 data files) — data/resend-queue.json
  131. 37f1708 2026-09-10 auto-data-snapshot: 2026-09-10T07:00:36 (1 data files) — data/runs/scheduled-09-10-2026.json
  132. 8d9e04b 2026-09-09 auto-data-snapshot: 2026-09-09T08:26:36 (1 data files) — data/resend-queue.json
  133. 6e0b6eb 2026-09-09 auto-data-snapshot: 2026-09-09T07:22:07 (1 data files) — data/runs/scheduled-09-09-2026.json
  134. 0fabe49 2026-09-08 auto-data-snapshot: 2026-09-08T08:24:00 (1 data files) — data/resend-queue.json
  135. 1961fd4 2026-09-08 auto-data-snapshot: 2026-09-08T07:07:04 (1 data files) — data/runs/scheduled-09-08-2026.json
  136. ee9b376 2026-09-07 auto-data-snapshot: 2026-09-07T08:09:05 (1 data files) — data/resend-queue.json
  137. 3034371 2026-09-07 auto-data-snapshot: 2026-09-07T00:25:13 (1 data files) — data/stamp-ledger.json
  138. 5b416dc 2026-09-06 auto-data-snapshot: 2026-09-06T08:25:53 (1 data files) — data/resend-queue.json
  139. 26b2bca 2026-09-05 auto-data-snapshot: 2026-09-05T08:08:29 (1 data files) — data/resend-queue.json
  140. 8b3d62e 2026-09-04 TK-10742: George-mined address spot-check (DTD-B safeguard) — 10/11 flags are legit rep-desk routing, 1 glance (maya)
  141. dc481ef 2026-09-04 auto-data-snapshot: 2026-09-04T08:26:29 (2 data files) — data/resend-queue.json data/stamp-ledger.json
  142. d72d0a3 2026-09-04 auto-data-snapshot: 2026-09-04T07:10:31 (2 data files) — data/draft-ledger.json data/runs/scheduled-09-04-2026.json
  143. c17d0e3 2026-09-03 auto-data-snapshot: 2026-09-03T08:35:39 (2 data files) — data/replies.json data/resend-queue.json
  144. ea35a72 2026-09-03 auto-data-snapshot: 2026-09-03T07:17:12 (1 data files) — data/runs/scheduled-09-03-2026.json
  145. 9892d18 2026-09-02 auto-data-snapshot: 2026-09-02T09:04:08 (1 data files) — data/stamp-ledger.json
  146. ac0c2e9 2026-09-02 auto-data-snapshot: 2026-09-02T08:25:36 (1 data files) — data/resend-queue.json
  147. f464e92 2026-09-02 auto-data-snapshot: 2026-09-02T07:10:19 (1 data files) — data/runs/scheduled-09-02-2026.json
  148. 993ca05 2026-09-01 auto-data-snapshot: 2026-09-01T13:36:10 (1 data files) — data/stamp-ledger.json
  149. 57d645d 2026-09-01 chore: surface FM stamp failures (no more silent catch), v1.1.0 (session close)
  150. 59409a0 2026-09-01 auto-data-snapshot: 2026-09-01T12:55:30 (2 data files) — data/stamp-ledger.json data/stamp-ledger.json.bak-1788291452
  151. 35ba693 2026-09-01 sample-followup: env knobs (SEARCH_DAYS/MAX_AGE) to backfill 2nd-request stamps
  152. 5ab8a3d 2026-09-01 sample-followup: FIX 2nd-request stamping — was silently stamping nothing
  153. ea9f7d9 2026-09-01 auto-data-snapshot: 2026-09-01T12:16:45 (5 data files) — data/contacts.json.bak-1788288111 data/draft-ledger.json data/draft-ledger.json.bak2-1788288802 data/runs/scheduled-09-01-2026.json data/stamp-ledger.json
  154. 9a219d8 2026-09-01 sample-followup: drop the [REVIEW] subject prefix — flag lives in the run report only
  155. 70ec596 2026-09-01 sample-followup: add vendor Ref/Tracking + Pattern-Name columns; add LBI Boyd contact
  156. 0a260b3 2026-09-01 sample-followup: drop DW ref# column, add explicit WIN override; scope drafts tight
  157. 4dfe1c2 2026-09-01 auto-data-snapshot: 2026-09-01T11:38:20 (3 data files) — data/draft-ledger.json data/draft-ledger.json.bak-1788287757 data/runs/scheduled-09-01-2026.json
  158. db9ca32 2026-09-01 sample-followup: --include-suppressed drafts the anti-dup-held vendors for review
  159. 3d869fe 2026-09-01 auto-data-snapshot: 2026-09-01T10:56:56 (1 data files) — data/runs/scheduled-09-01-2026.json
  160. 38d86d1 2026-09-01 sample-followup: auto-widen window to last successful run (belt-and-suspenders)
  161. f546361 2026-09-01 sample-followup: chase today's 10-day cohort today; Tue folds in Sat/Sun/Mon
  162. c1ce8fa 2026-09-01 sample-followup: make Tuesday window weekday-aware (cover Fri/Sat/Sun/Mon -10)
  163. c024fa0 2026-09-01 auto-data-snapshot: 2026-09-01T08:28:16 (1 data files) — data/resend-queue.json
  164. b2d2c8e 2026-09-01 auto-data-snapshot: 2026-09-01T07:51:05 (1 data files) — data/stamp-ledger.json
  165. d1494f3 2026-09-01 auto-data-snapshot: 2026-09-01T07:13:20 (2 data files) — data/draft-ledger.json data/runs/scheduled-09-01-2026.json
  166. 956f3d8 2026-08-31 auto-data-snapshot: 2026-08-31T12:34:16 (1 data files) — data/stamp-ledger.json
  167. 985d96c 2026-08-31 auto-data-snapshot: 2026-08-31T08:15:07 (1 data files) — data/resend-queue.json
  168. eb0c3fa 2026-08-30 add deterministic sample contact triage guard
  169. 2c52a29 2026-08-30 auto-data-snapshot: 2026-08-30T08:29:04 (1 data files) — data/resend-queue.json
  170. 129af57 2026-08-29 auto-data-snapshot: 2026-08-29T08:29:29 (1 data files) — data/resend-queue.json
  171. f171037 2026-08-28 Reject injected mailto opt-out headers
  172. 900aa26 2026-08-28 Require visible opt-out link labels
  173. 1f59efb 2026-08-28 Parse compliance HTML before validating visibility
  174. 9c15a26 2026-08-28 auto-data-snapshot: 2026-08-28T15:48:59 (2 data files) — README.md package-lock.json
  175. 0a4357f 2026-08-28 auto-data-snapshot: 2026-08-28T15:12:09 (1 data files) — package.json
  176. a3c2bbb 2026-08-28 Record cycle 3 compliance proof identity
  177. 297d26a 2026-08-28 Require visible owned compliance content
  178. 984ab8e 2026-08-28 Centralize sample follow-up compliance enforcement
  179. c70d49a 2026-08-28 auto-data-snapshot: 2026-08-28T14:34:59 (1 data files) — README.md
  180. c74ebf7 2026-08-28 Add fail-closed sample follow-up compliance gate
  181. 7dd3faf 2026-08-28 auto-data-snapshot: 2026-08-28T10:53:29 (1 data files) — data/stamp-ledger.json
  182. 7cc9f87 2026-08-28 auto-data-snapshot: 2026-08-28T08:24:14 (1 data files) — data/resend-queue.json
  183. 72dda45 2026-08-28 auto-data-snapshot: 2026-08-28T07:08:13 (1 data files) — data/runs/scheduled-08-28-2026.json
  184. 422854f 2026-08-27 auto-data-snapshot: 2026-08-27T10:10:28 (1 data files) — data/stamp-ledger.json
  185. 039712f 2026-08-27 auto-data-snapshot: 2026-08-27T08:19:36 (1 data files) — data/resend-queue.json
  186. 9ba501a 2026-08-27 auto-data-snapshot: 2026-08-27T07:06:07 (2 data files) — data/draft-ledger.json data/runs/scheduled-08-27-2026.json
  187. 2e2e947 2026-08-26 auto-data-snapshot: 2026-08-26T08:21:31 (1 data files) — data/resend-queue.json
  188. bf752cc 2026-08-26 auto-data-snapshot: 2026-08-26T07:08:04 (1 data files) — data/runs/scheduled-08-26-2026.json
  189. 541e2af 2026-08-25 chore: sent-poller stamps per-pass date (midnight-safe for 24/7 loop), session-close quality gate
  190. d5a63da 2026-08-25 sample-followup: rewrite sent-poller to match by Acct# from subject + rolling 10-60d window + idempotent (skip already-stamped); install persistent launchd job (30s poll) per Steve — auto-stamps 2nd-request date for Gmail-sent follow-ups
  191. 29048f5 2026-08-25 sample-followup: correct Elitis contact to US rep ANNALISA@elitis.us (Steve sent there, not France HQ)
  192. 32145b7 2026-08-25 sample-followup: on real send, auto-stamp 2nd-request date (Date Sample Request Letter Sent) in all 3 send paths (fmpro console send, scheduled --send, sent-poller) — Steve 8/25
  193. 93219aa 2026-08-25 auto-data-snapshot: 2026-08-25T10:08:44 (4 data files) — data/contacts.json.bak-20260825-0957 data/draft-ledger.json data/fleet.json.bak-20260825-0957 data/runs/scheduled-08-25-2026.json
  194. 48c2f8e 2026-08-25 sample-followup: resolve + wire 4 no-contact vendors (Paul Montgomery, MDC, Elitis, Lincrusta) — main-email fallback per Steve
  195. a18ec74 2026-08-25 auto-data-snapshot: 2026-08-25T08:16:28 (2 data files) — data/replies.json data/resend-queue.json
  196. 1e41c3d 2026-08-25 auto-data-snapshot: 2026-08-25T07:02:59 (1 data files) — data/runs/scheduled-08-25-2026.json
  197. e2f3ae2 2026-08-24 TK-10743: contact-resolution triage of skipped sample-followup vids + prepare-only ready queue
  198. 7539547 2026-08-24 auto-data-snapshot: 2026-08-24T08:30:43 (1 data files) — data/resend-queue.json
  199. cfda8af 2026-08-23 auto-data-snapshot: 2026-08-23T08:35:32 (1 data files) — data/resend-queue.json
  200. 4543ad4 2026-08-22 auto-data-snapshot: 2026-08-22T08:08:28 (1 data files) — data/resend-queue.json
  201. 3c98218 2026-08-21 auto-data-snapshot: 2026-08-21T08:13:58 (2 data files) — data/replies.json data/resend-queue.json
  202. b8a2c15 2026-08-21 auto-data-snapshot: 2026-08-21T07:10:15 (2 data files) — data/draft-ledger.json data/runs/scheduled-08-21-2026.json
  203. 660caf0 2026-08-20 auto-data-snapshot: 2026-08-20T14:07:04 (2 data files) — data/draft-ledger.json data/outstanding-snapshot.json
  204. 55eebea 2026-08-20 send-reconciled: rolling catch-up window + draft-dedup ledger (seeded w/ today's handled batch) — make it daily-safe as the authoritative morning drafter
  205. b6a575a 2026-08-20 auto-data-snapshot: 2026-08-20T13:34:43 (1 data files) — data/runs/scheduled-08-20-2026.json
  206. 2dfaa6c 2026-08-20 sample-followup: self-updating suppression (live info@ Sent query via George) + 7-day rolling catch-up window; drafts stay non-duplicative hands-free
  207. 405e7de 2026-08-20 watch-sent-stamp: fix Sent search (drop em-dash from subject query) + drop unwritable 2nd Request field (Date-Email-Sent is the marker)
  208. 55d6a33 2026-08-20 auto-data-snapshot: 2026-08-20T11:21:15 (1 data files) — data/stamp-ledger.json
  209. 9f0c85d 2026-08-20 watch-sent-stamp: fast Sent-poller stamps FileMaker 2nd Request (+Date-Email-Sent) when a follow-up actually leaves
  210. 5df1ebe 2026-08-20 reconciled drafter: window 8/6-8/10 (fill-in since last send), Sent-verified addrs + processor, no Ref# on emails; draft into info@
  211. aacc67a 2026-08-20 sample-followup: recently-contacted suppression — skip vendors the team already emailed since 8/15 (harvested from info@ Sent); redo drafts non-duplicative (26->3 vendors)
  212. 4faf95e 2026-08-20 auto-data-snapshot: 2026-08-20T10:47:55 (2 data files) — data/draft-ledger.bak-20260820-1040.json data/draft-ledger.json
  213. 9e48b0a 2026-08-20 letter: show Ref # (DW order ref) + Date Ordered per memo line so suppliers can locate/quote each item
  214. 2961d0e 2026-08-20 run: 26 Gmail drafts created via --draft (9 newly-enriched vendors + 17 backlog); ledger deduped to 85 recordIds. TK-10742 deliverable — Steve sends from Drafts, NOT sent
  215. ae6499b 2026-08-20 enrich: +15 vendor sample contacts from George/Gmail (SIR COW BM AMER EGG COOR HYG CL-Europe) + no-chase (FOL/PhilipperRomano/Carl) + unresolved-identity (FTR/ULR/1838/POI); fix TWI acct; fleet stubs so vidContactMap resolves. All needs_confirm w/ contact_source. TK-10742
  216. b62f3d9 2026-08-20 sample-sender: draft mode + 3-day exact-10d catch-up window (Steve) + BM->Greenland alias; 1 draft created
  217. 3aa59b2 2026-08-20 Sample follow-up: bound window to a rolling 3-day catch-up band at the 10-day mark (only memos that just crossed 10d, not the whole back-catalog)
  218. 40a48ce 2026-08-20 auto-data-snapshot: 2026-08-20T10:13:33 (2 data files) — data/runs/scheduled-08-20-2026.json launchd/com.steve.sample-followup-send.plist
  219. 672dda2 2026-08-20 resolve 8 vendor sample desks from Gmail (held-for-confirm) + Schumacher no-chase; skip gap 44->36
  220. dcd9edf 2026-08-20 sample-sender: main-email fallback + HELD-for-confirm guard + Anna French routing + 10-day/5yr window
  221. 02128e4 2026-08-20 auto-data-snapshot: 2026-08-20T09:38:44 (2 data files) — data/fleet.json data/runs/scheduled-08-20-2026.json
  222. 1b2212f 2026-08-20 notes-queue: Sandberg + Sanderson samples ordered online (not memo) — skip from chase
  223. 45565c8 2026-08-20 auto-data-snapshot: 2026-08-20T08:33:21 (1 data files) — data/resend-queue.json
  224. 094d74a 2026-08-19 auto-data-snapshot: 2026-08-19T08:31:04 (2 data files) — data/replies.json data/resend-queue.json
  225. 5edba21 2026-08-18 auto-data-snapshot: 2026-08-18T08:10:11 (2 data files) — data/replies.json data/resend-queue.json
  226. 38b88cf 2026-08-17 auto-data-snapshot: 2026-08-17T09:25:02 (1 data files) — data/runs/scheduled-08-17-2026.json
  227. 54155fb 2026-08-17 Scheduled Tue-Fri 7am auto-sender: send un-chased >10-day follow-ups, stamp=dedup
  228. e4bd94a 2026-08-17 auto-data-snapshot: 2026-08-17T09:04:04 (1 data files) — data/runs/scheduled-08-17-2026.json
  229. e39bcb3 2026-08-17 Notes: write the 5 held ambiguous replies to Priority Memo Notes (Steve-approved); clear notes-queue
  230. 8b1f69d 2026-08-17 Add daily reply-poller launchd plist (com.steve.sample-followup-poll, 8:05am PT) — Steve loads it
  231. a7de739 2026-08-17 Priority Memo Notes: write 21 clear vendor-reply notes to FileMaker; queue 5 ambiguous for review
  232. 6a7ae10 2026-08-17 Reply loop: poll info@ for vendor replies + 3-business-day one-click resend queue
  233. de3da21 2026-08-17 FMPro write-back: stamp Date Email Sent to Vendor after 10 Days on send + backfill engine + console FMPro chip
  234. eabe843 2026-08-15 auto-data-snapshot: 2026-08-15T15:50:00 (1 data files) — data/sent.json
  235. df1406f 2026-08-15 yoloforever c8: sent Greenland + Olivia&Poppy follow-ups (Steve pre-approved); held Pierre Frey (unconfirmed rep, needs Steve OK)
  236. e1ab42d 2026-08-15 yoloforever c6: triage Aug-14 follow-up replies (Maya discontinued / Kravet delivered / Pindler mis-vendored); gated FM actions drafted to pending-approval
  237. 9eed1e1 2026-08-15 yoloforever c4: misfile sweep — no real cross-vid misfiles (shared house-series overlaps); RAD->no-chase (DIG prefix=Spoonflower-family digital)
  238. 81464fe 2026-08-15 yoloforever c3: fix client-JS regex-in-template-literal error (render was dying); no-chase badge + garbage-mfr flag now verified live
  239. adaac2f 2026-08-15 yoloforever c3 (Cody): console honors no-chase (badge+grey Spoonflower), flags garbage mfr rows (*** ENTER MFR #), build-fleet SCHUMACHER->SCH vid merge
  240. e169d6d 2026-08-15 yoloforever c2: research vendor contacts (web+registry+sent); write Pierre Frey (Kneedler Fauchère, DW-sourced), Spoonflower no-chase, verified gap report
  241. 4c4c19c 2026-08-15 yoloforever c1: ledger + drafts-ready state
  242. 46f2853 2026-08-15 auto-data-snapshot: 2026-08-15T08:12:28 (1 data files) — data/contacts.json
  243. e8bc50a 2026-08-15 yoloforever c1: Cody FIX-FIRST — sent.json freshness indicator + build-fleet date fix
  244. 29a0213 2026-08-15 Console: per-vendor Send button (fires exact letter via George token, human-click)
  245. 2397a7f 2026-08-15 Console: exact-letter preview + populate Sent snapshot (18 letters sent 8/14); 25 memo stamps in FileMaker
  246. 2678e39 2026-08-14 Add send-drafts.js (Steve-triggered sender via George approval token)
  247. b402931 2026-08-14 16 vendor follow-up drafts created; fill blanks via primary vendor addresses (Concertex, Sandberg/Rebel Walls via Gimmersta)
  248. 658a9b1 2026-08-14 Harvest page 2: +9 vendor contacts (astek, arte, pindler, maya, twill, o&p, sancar, china seas, lee jofa)
  249. cbdaa5d 2026-08-14 Harvest vendor sample emails + accounts from Sent folder into console (15 vendors ready)
  250. 55d6879 2026-08-14 Console: re-request detection + initial-request date, expandable item detail, pinned port
  251. d5b7022 2026-08-14 Fleet sweep (37 vendors/106 items) + selection console (server.js)
  252. d8d9ad8 2026-08-14 Add 60-day dead-lead ceiling + next vendor (Scalamandre) from live sweep
  253. a51c7d2 2026-08-14 Sample Follow-Up Sweep — phases 1-4 engine + canary vendor (O&L)

Authors

Agents used

  • vp-dw-commerce1

Skills used

  • /stamp-ledger45
  • /resend-queue42
  • /runs37
  • /draft-ledger20
  • /contacts20
  • /claude13
  • /code13
  • /outstanding-snapshot10
  • /coverage-reconcile8
  • /latest7
  • /reconciled-draft-ledger7
  • /sweep6
  • /fleet6
  • /scheduled-09-10-20266
  • /replies6
  • /scheduled-09-01-20264
  • /scheduled-09-23-20263
  • /scheduled-09-15-20263
  • /scheduled-08-20-20263
  • /sample-followup2
  • /slug-aliases2
  • /scheduled-09-12-20262
  • /build-fleet2
  • /scheduled-run2
  • /whitespace2
  • /fmpro2
  • /send2
  • /scheduled-08-25-20262
  • /scheduled-08-17-20262
  • /state2

Creative ideas + design notes

Commits with substantial prose (≥120 chars) — the rationale behind each move.

5b98d5b · 2026-09-26 · Sent-stamp watcher: dead-letter heartbeat + vid-keyed pull-forward (TK-12351)
Heartbeat to ~/.claude/skills/sample-followup/data/latest.json after each live pass
(PASS/WARN/FAIL): WARN on unresolved sends within 5d of aging out of the 21d search or
aged out in the last 7d, or when the pass could not measure; FAIL when a pass throws.
--dry prints it and never writes. Pull-forward date ownership now keys on any shared
resolved vid (Lincrusta sample vs main email), address fallback when unmapped. Unreadable
ledger now fails closed instead of overwriting it. Skip entries record sent_on.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDCCv6ZcKPKfWxof41UzvC
d523fb1 · 2026-09-26 · sent-stamp watcher: fold review findings (never overwrite a chase date)
- covered-only is no longer terminal: a sibling item in the same letter
  may not be in FileMaker yet, so the send keeps retrying.
- per-record read-before-write: skip any record that already carries a
  chase date, independent of the already-stamped set; refuse to stamp a
  send if that set hit its 800-row cap.
- an older unfinished send in backoff is pulled forward when a newer send
  to the same recipient is due, so the older letter keeps its date.
Tests extended; negative fault injection still goes red. TK-12320

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDCCv6ZcKPKfWxof41UzvC
523a32c · 2026-09-26 · watch-sent-stamp: refuse unknown args instead of running a live pass (TK-12320)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbUKLFLXi7LEQcGyGeYqp9
a6b53a0 · 2026-09-26 · sent-stamp watcher: retry skipped/zero sends, stamp the real send date
A ledger entry now only suppresses re-processing once the send actually
stamped (or found its letter items already stamped) with no failed commit.
skip / stamped:0 sends are retried (15 min backoff) inside a 21-day Sent
scan, so sends that landed before their email->vid mapping existed are no
longer poisoned forever. The chase date is the message's send date
(internalDate), sends are processed oldest-first so the first letter owns
the date, FM/George lookup errors no longer masquerade as 'nothing to
stamp', and contact-only vendors (Phillip Jeffries) map via the
slug-derived vid. Pure logic moved to sent-stamp-core.mjs with a negative
test. TK-12320

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDCCv6ZcKPKfWxof41UzvC
616e3a8 · 2026-09-24 · sample-followup: fix TK-12091 window regression + shared window + canary tripwire
Part 1 (DTD panel verdict A): restore the FULL active [today-60..today-10] query
window every run. TK-12091 (a11a644) had narrowed scheduled-run.mjs to a
day-of-week band, silently reopening TK-12013/14's permanent-hole bug — a live
canary found 15 outstanding stragglers (some 56d old, incl. DWTT70793) never
drafted. Dedup (Sent-stamp + draft-ledger) makes the wide re-query a safe no-op.

Part 2 (Cody's co-required fix): single source of truth for the window —
followupWindow() in lib/sweep.js, called by BOTH scheduled-run.mjs and the
coverage canary, so production and audit can never drift again. Canary now runs
windowCoversActiveBand() as a TK-12091-regression tripwire: if production's
window is ever re-narrowed below the full active band, the canary goes FAIL
instead of silently auditing a wider window than production chases. Ships a
negative test (test 8) proving the tripwire goes red on an injected narrow window.

Verified: window now 07/26..09/14; DWTT70793/Thibaut back in candidate set;
canary leaked=0, band covers=true; 8/8 tests pass. sweep()'s orphaned DOW logic
is dead code (nothing calls it) — left untouched, noted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
83b47fa · 2026-09-24 · sample-followup: LA Walls/DWLC1059 tracked as known-unresolvable (TK-12119)
Add DWLC1059 (LA Walls pattern 23003) to known-unresolvable set — vendor has
no vid anywhere in the fleet, so sweep can't chase it. Bucket it as
noVendorMap (accounted/WARN) instead of leaked (FAIL). Canary verdict:
FAIL → WARN, leaked 1→0. Reversible: remove SKU from set or delete set entry.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8c3b575 · 2026-09-24 · sample-followup: fix tk12118 SKU guard to verify on REPORT ON SAMPLES ORDERED (TK-12118)
'combo sku' is findable but does not serialize into fieldData on the write layout
(Report for old memo samples), so the SKU-match guard always saw "" and skipped.
Verify the SKU on REPORT ON SAMPLES ORDERED (same base table, same recordId space,
field reads correctly); write vid on the write layout; verify the write via the
canary leaked-count (vid does not read back on either layout). Backfill landed:
canary leaked 4->1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
32a76cf · 2026-09-24 · sample-followup: canary no longer counts deferred recipient-busy rows as covered (TK-12119)
Split the recipient-busy suppression by age: a fresh batching deferral (<=24d =
10-day trigger + one 14-day window) counts as covered, but a row deferred past
that window escalates to recipientBusyStale -> uncovered -> WARN. Closes the
false-green where a perpetually-recontacted vendor's never-chased sku hid as
"covered." First live run surfaced 33 real stale-deferred rows. Ships a negative
test proving the aged row flips from the old covered/PASS to WARN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1d2b680 · 2026-09-23 · sample-followup: coverage-reconciliation canary (silent-skip false-green safety net) — TK-12117
READ-ONLY scripts/coverage-reconcile.mjs queries live FileMaker for the true
outstanding population (two-read vid+letterSent join) and reconciles it against
the sweep's own bucketing (lib/sweep.js reconcileCoverage). A row outstanding in
FM but in NO sweep bucket = LEAKED = FAIL; never PASS on 'it ran'. Emits
PASS/WARN/FAIL + latest.json. Ships test/coverage-reconcile.test.cjs (5 asserts,
incl injected-leak FAIL) + a --self-test seam. First live run FOUND 4 real
empty-vid silent skips (FAIL). launchd install drafted to pending-approval (gated).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
473c034 · 2026-09-23 · sample-followup: resolve MAJ (Majilite) + BRAND (Brand McKenzie) vendor-map holes (TK-12114)
MAJ = Majilite -> jabshire@majilite.com (James Abshire), confirmed by DW's own
repeated 'New Sample Request' outbound (2026-09-10/21/22) + replies. BRAND =
Brand McKenzie -> sales@sancarwallcovering.com (Sancar acct 1674444, its US
distributor; replies to our sample requests/follow-ups), needs_confirm w/ direct
richard@brandmckenzie.com as documented alternate. no-vendor-map count 2 -> 0.
DRAFT/dry-run only, nothing sent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
05177f2 · 2026-09-23 · sample-followup: record 3 manually-sent 10-day letters + gated FM-stamp script (TK-12109)
Steve sent FABR/WAT/GRD 10-day letters manually from info@ (verified in Sent).
recently-contacted.json += the 3 recipients (14-day anti-dup now reflects it).
Added scripts/tk12109-stamp-letter-sent.mjs (+ --rollback) to stamp FM
Date Sample Request Letter Sent=09/23/2026 on the 3 outstanding records
(541857/541215/541860) with a restore-map — gated to Steve (classifier blocks
the FM write). BAR held (not sent), REID arrived, MDCKEN no-chase — not stamped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6c9b709 · 2026-09-23 · sample-followup: draft the 5 verified-owed 10-day SKUs + CONFIRM-RECIPIENT banner/ledger mark (TK-12107)
send-reconciled.mjs draft() now returns the George draftId; needs_confirm vendors
get an INTERNAL 'CONFIRM RECIPIENT' body banner + a '⚠ CONFIRM RECIPIENT' ledger
mark so Steve fixes To: before send. Created 4 drafts (5 SKUs): FABR customers@
fabricut.com, WAT sophie@watts1874.co.uk [confirm], BAR studio@/alice@barnebygates.com
x2 [confirm], GRD orders@graduatecollection.co.uk [confirm]. REID guard-suppressed
(arrived), MDCKEN no-chase. Drafts only — nothing sent, no FM write.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
d04d106 · 2026-09-23 · sample-followup: two-part never-false-chase guard (arrival-dedup + already-sent) — TK-12105
Before chasing a sku, pull ALL FileMaker records ('Report for old memo samples')
for that combo sku and suppress if EITHER (a) a sibling with the SAME entered date
already has Date WP Sample Sent (arrived on a duplicate record — catches REID
DWRW210156, spares GRD8806 2024-vs-2026), or (b) any record has Date Sample
Request Letter Sent (Steve's authoritative 10-day-letter stamp). Pure suppressChase()
in lib/sweep.js (+4 unit tests), wired into scheduled-run.mjs + send-reconciled.mjs
with a new guard-suppressed bucket. Fail-open; 14-day Gmail anti-dup kept. Nothing sends.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7534717 · 2026-09-23 · sample-followup: exclude MDCKEN as non-vendor no-chase (TK-12101)
Steve 2026-09-23: MDCKEN is not a vendor. Added contacts.json disposition
no-chase entry (same mechanism as SPN/Spoonflower) so MDCKEN (DWPP rows)
resolves as no-chase BEFORE the 'no vendor map' path — never chased, never
drafts, never surfaces as blocked/unresolved. Verified in both scheduled-run.mjs
and send-reconciled.mjs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
a11a644 · 2026-09-23 · sample-followup: day-of-week-aware window (Tue 4-day catch-up, Wed-Fri strict) — TK-12091
Replaces the pure single-day filter (e3a7a73). windowRange (scheduled-run.mjs),
WIN (send-reconciled.mjs), and lib/sweep.js now: hi=today-10 always; TUE widens
lo to today-13 (covers Sat/Sun/Mon/Tue 10th-day landings), Wed/Thu/Fri (+manual
off-days) strict single day. New test/sweep-dow.test.cjs asserts (a) Tue picks
Sat/Sun/Mon/Tue cohorts, (b) Wed/Thu/Fri pick only today-10, (c) Fri->Tue->Wed
selects every Entered date exactly once (no gap/dup). WIN/CATCHUP/WIDEN_DAYS kept
as manual recovery nets. pre-send-gate + human batch-send untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
e3a7a73 · 2026-09-23 · sample-followup: single-day rolling window (Entered==today-10), revert >10d backlog (TK-12087)
scheduled-run.mjs windowRange, send-reconciled.mjs WIN, and lib/sweep.js now
chase ONLY the cohort entered EXACTLY minAgeDays(10) calendar days ago instead
of the [10..60] cumulative backlog introduced by TK-12013/12014. WIDEN_DAYS/
CATCHUP/WIN remain as explicit manual recovery nets.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7394a41 · 2026-09-23 · sample-followup: resolve 68 blocked overdue rows (TK-12085)
send-reconciled.mjs now falls back to contacts.json via slugForVid (single
source of truth) + strips \r from vids (KRA\r bug); adds fabr/wat/reid/grd,
corrects ftr->shafik@futuretextiles.com, 1838 email confirmed. 19 vendor
drafts created (DRAFT-only); MDCKEN(8) unresolved, SPN(2) no-chase.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
99d3128 · 2026-09-22 · fix(fleet): fall back to contacts.json vendor name before the (vid X) placeholder
Display-only. build-fleet.js name expressions (both the group row and the
alias-stub map) rendered '(vid GREEN)' because GREEN isn't in the NAME map,
even though contacts.json has green -> 'Greenland (bmwallpaper)'. Now falls
back to the contacts name keyed by slug. Helps any vendor with a contacts
name. Regenerated fleet.json: GREEN row label fixed, its 13 items / vid /
slug / aliasVids UNCHANGED. No send, no FileMaker write, no re-attribution.

TK-12013
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UBN9bgoXhsUaDnGkXU1T6m
89f8650 · 2026-09-22 · refresh fleet.json from live FM sweep + declare 1838 WALLCOVERINGS slug alias
fleet.json (the :64845 console's vendor-row source) was 28 days stale
(Aug 25). Rebuilt from a fresh 10-60d outstanding sweep so all current due
vendors render as approvable rows. Added the '1838 WALLCOVERINGS' -> 1838-
wallcoverings alias the build-fleet dup-slug guard required (same vendor).

TK-12013
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UBN9bgoXhsUaDnGkXU1T6m
5374f08 · 2026-09-22 · fix(window): query full active [today-60..today-10] cohort every run
The auto-widen anchor only reached back to the last SUCCESSFUL run
(lastCoveredHi=09/02 -> lo 09/03), so older still-outstanding, still-unchased
memos (07/24-09/02) fell outside the window and were never chased; a run of
missed days made the hole permanent. send-reconciled used a today-16..today-10
band with the same defect. Both now query the full active window; the FileMaker
Sent-stamp set-difference (scheduled-run) / draft-ledger (send-reconciled) is the
dedup, so re-covering already-chased memos is a no-op and a missed run day can
never drop the older cohort.

TK-12014 / TK-12013
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UBN9bgoXhsUaDnGkXU1T6m
fcd34dc · 2026-09-22 · fix(sweep): include exactly-10-day-old requests (age < minAge, was <=)
The >10-day rule excluded requests aged exactly 10 days (a 9/12 request due
today was skipped until age 11). Steve: exactly-10 must be INCLUDED.
lib/sweep.js:36 and build-fleet.js:41 both had the age<=10 off-by-one.

TK-12014 / TK-12013
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UBN9bgoXhsUaDnGkXU1T6m
be622a9 · 2026-09-12 · TK-11524: close sample-followup no-email gap (item d)
- lib/slug-aliases.cjs: add SCHUMACHER->sch to the single-source-of-truth
  SLUG_ALIASES. The merge previously lived only in build-fleet.js's local
  VID_ALIAS, so scheduled-run.mjs's runtime contact bridge mapped raw vid
  SCHUMACHER->slug 'schumacher' (no contact) -> CONFIRM-SAMPLE-EMAIL skip-gap
  (the two-copies-diverge class the file header warns about, cf SAND->Sandberg).
- data/contacts.json ulr: resolve Ulrich Welter -> info@welter-wandunikate.de
  (Welter Wandunikate; verified via Gmail two-way order thread msg 17e4fa0f0ecd5b7d),
  HIGH confidence, held needs_confirm:true pending Steve confirm (no auto-send).
- data/contacts.json tbd: disposition no-chase (FileMaker placeholder vid, not a
  real vendor; real fix = correct the source memo vid in FileMaker, gated).

Verified: contacts.json valid, build-fleet.js parses, slugForVid(SCHUMACHER)=sch,
0 undispositioned no-email slugs remain (was 6).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3SgrKtLCZRzmFK5fp9qy8
8e1807b · 2026-09-12 · lib/slug-aliases.cjs: one source of truth for slug routing — the two copies had already diverged (TK-11255)
The alias table lived in TWO hand-maintained places: scripts/build-fleet.js
(grouping) and scripts/scheduled-run.mjs (the vid->contact bridge). They had
ALREADY drifted apart — only the bridge knew that bare vid '1838' is the same
vendor as '1838 Wallcoverings'. Two copies of one routing table is precisely the
failure mode that produced SAND->'Sandberg' and sent Sanderson memo chases to
Gimmersta, an unrelated company, earlier in this ticket. Flagged it as a smell in
6771f15; the drift was already there when I looked.

Now exactly one definition exists in the repo (verified by grep), exporting
SLUG_ALIASES, normVid and slugForVid.

Two real bugs fixed by consolidating rather than by intent:
  - build-fleet.js never stripped CR/whitespace from vid either, so a record with
    vid "kra\r" grouped under its own bogus slug. It now shares normVid() with
    scheduled-run, which got that fix in e343b5f.
  - build-fleet.js lacked the '1838' alias, so the bare vid grouped separately
    from '1838 Wallcoverings'. Both now converge on '1838-wallcoverings'.

Re-tested after the change, not assumed — including the negative test, because a
consolidation that quietly disarms the guard is the worst outcome here:
  T1 distinct vids          -> writes
  T2 ANNA FRENCH with items -> merges into thib, no stray anna-french slug
  T3 'FOO BAR' + 'FOO-BAR'  -> exit 1, fleet.json NOT written (guard still reddens)
  ESM -> CJS import proven at RUNTIME, not just by node --check, since
  scheduled-run.mjs is ESM and this module is CJS.
Live re-verification through the REAL shared module (not a reimplementation of
its logic): 27 of 28 live vendors routable, 129 overdue records, only FUT
unresolved — unchanged, so consolidation regressed nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5c0d68e · 2026-09-12 · contacts.json: route VAH + BRU + bare-1838 — 27 of 28 live vendors now reachable (TK-11255)
Closes most of the silent-skip gap measured today: of 129 live overdue sample
records, 11 across 7 vendor codes resolved to NO contact and were dropped with no
error and no log line.

  vah  Vahallan -> info@vahallan.com, acct "On File"
       Verified live before adopting: Annie Kaufman, Senior Sales Coordinator,
       replied "Thank you for your sample request. We will get these sent out"
       to our own request YESTERDAY (2026-09-11) and again 2026-09-03.
  bru  Brunschwig & Fils (@ Kravet) -> matt.schoffman@kravet.com, acct 10087117
       Canonical says "Brunswig and fils @ Kravet" and Brunschwig is a Kravet
       house, so it shares Kravet's desk and account. Held to the same bar
       rather than assumed: Matt Schoffman replies to our sample requests with
       UPS tracking, most recently 2026-09-10.
  1838 bare vid routed to the existing 1838-wallcoverings contact via
       SLUG_ALIASES — same vendor (Scott Myer), so this is a routing alias
       rather than a duplicated contact record.

Measured effect, live: routable vendors 24 -> 27 of 28; unroutable records
8 -> 2.

NOT adopted: FUT (2 records). Canonical has no FUT row. It is probably the same
vendor as FTR ("future Textile", ftg1@futuretextiles.com) on a name-shaped hunch,
and that is exactly the kind of guess that put Sanderson memos at Gimmersta
earlier in this ticket. It stays unroutable until something confirms it.

Both new accounts use the house "On File"/inherited-account convention rather
than an invented number, and the scheduled job runs --draft, so the effect is a
reviewable draft per vendor, never an automatic send.

Undo: restore data/contacts.json.bak-1789224483

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6771f15 · 2026-09-12 · scheduled-run: decouple the vid->contact bridge from the fleet snapshot (TK-11255)
fleet.json does DOUBLE DUTY: it is the outstanding-items snapshot (which
legitimately shrinks as samples arrive) AND the vid->contact bridge (which must
stay complete). vidContactMap built the bridge only from fleet rows, so a vendor
with no CURRENT overdue items had no contact — and the next time one went
overdue it was silently skipped until someone regenerated the file. Nobody
regenerates it: no launchd job runs build-fleet.js and scheduled-run only reads.

This was measured, not theorised. I regenerated into a SANDBOX and diffed:
  gains 4 vids (1838, BRU, FUT, VAH)
  DROPS 37 (PJ, WOL, PF, SCH, MOR, THI, ELI, LIN, RAD, TWI, KOR, ...)
A naive regeneration would therefore have inflicted the exact bug I was chasing
on 37 vendors to fix it for 4. The regenerated file was NOT promoted.

Fix: slug is derivable from vid, so the bridge can go straight to contacts.json.
vidContactMap now takes the live vid set and UNIONs fleet-derived entries with
slug-derived ones. Union only — it cannot make a vendor that resolves today stop
resolving. Derivation runs from the live VID, never backwards from the slug,
because slug->vid is lossy ('1838-wallcoverings' could come from a space or a
hyphen).

HONEST EFFECT TODAY: zero vendors recovered. Verified live — resolvable is 24 of
28 both before and after, because e343b5f (vid \r trim) and a1c761a (POI, 1838)
already recovered everything recoverable. The value here is that regenerating
fleet.json is now SAFE, and a vendor dropping out of the outstanding list no
longer quietly loses its contact. Prevention, not a fix.

Still unroutable: 1838, FUT, VAH, BRU — 8 overdue records. Those need
contacts.json entries, not code.

Known smell, deliberately not addressed here: SLUG_ALIASES now exists in both
build-fleet.js and scheduled-run.mjs. Two copies of one routing table is exactly
the failure mode that produced SAND->Sandberg; it wants consolidating into one
shared module.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
a1c761a · 2026-09-12 · contacts.json: unblock POI + 1838, both silently skipped by the mailer (TK-11255)
Measured against LIVE FileMaker: 11 of 129 overdue sample records in the current
10-60 day window resolve to no contact and are skipped with no error and no log.
These are two of them, and both are now recoverable.

  poi                -- already had samples@justindavid.com as of today, but
                        vidContactMap requires email AND account_number, so it
                        was still being skipped. Canonical carries a blank
                        account, so it takes the house "On File" convention
                        (already used by 32 of 58 contacts: pj, mor, green,
                        maya, twi, olp, con...).
  1838-wallcoverings -- canonical VID '1838' = scott.myer@gmail.com. Verified
                        live before adopting: Scott replied "memo has been
                        ordered. thank you scott" to our own sample request on
                        2026-09-01, and an earlier thread quotes stock and euro
                        roll pricing. A personal gmail looks wrong for a vendor
                        desk, but it clears the hardened bar on its own evidence
                        (human-authored, sample-topical, current) and matches the
                        known small-brand pattern already accepted for twi.

Note account_number is customer-facing -- send-reconciled.mjs prints "Our account
number is X" -- so "On File" is a deliberate reuse of the existing convention,
not an invented number.

Safe by construction: the scheduled job runs scheduled-run.mjs --draft, and the
code is explicit that "Nothing sends -- the draft is still the human gate." These
two vendors now produce a reviewable draft instead of silently producing nothing.

Still unroutable and NOT touched here: VAH, BRU, 1838 (bare vid) and FUT are
absent from data/fleet.json entirely, so a contacts entry alone cannot reach them
-- vidContactMap iterates fleet rows. They need the fleet regenerated first.

Undo: restore data/contacts.json.bak-1789224232

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
e343b5f · 2026-09-12 · sample-followup: strip CR/whitespace from FileMaker vid — it was silently dropping chases (TK-11255)
Measured live against FileMaker, not hypothesised: of 129 overdue sample records
in the current 10-60 day window, 11 across 7 vendor codes resolve to NO contact
and are skipped with no error and no log line. One of those is a plain key bug.

A Kravet record's vid is literally "kra\r". scheduled-run.mjs keyed the contact
map with String(vid).toUpperCase() and no trim, so "KRA\r" never matched "KRA"
and that vendor's overdue sample was never chased. Same untrimmed pattern in
resolve-original-dates.mjs. build-fleet.js already strips \r from Mfr Pattern and
client name — vid was simply missed.

This is the silent-skip class: nothing errors, nothing logs, the sample just
never gets chased and a client waits.

Both sites now strip CR/LF and trim before upper-casing.

The other 6 skipped vids are NOT this bug and are deliberately not touched here:
  1838, FUT, VAH, BRU  -- vid absent from data/fleet.json, which is 29 days stale
                          and is regenerated by nobody (no launchd job runs
                          build-fleet.js; scheduled-run only READS the file).
                          These are vendors whose samples went overdue after the
                          last build. Fixed by regenerating, now that d8486af
                          makes regeneration safe for the alias stubs.
  POI                  -- has an email as of today but no account_number, and
                          vidContactMap requires BOTH.
  1838 WALLCOVERINGS   -- no email, no account, and no canonical FileMaker row.

Note the item list itself is NOT stale — scheduled-run pulls it live from
FileMaker each run. Only the vid->contact bridge is stale, so the failure mode
is silently skipping a vendor, never chasing a dead lead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
eafdc77 · 2026-09-12 · chore: v1.1.0 -> v1.1.1 (TK-11255 vendor-desk routing fix, session close)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSLggdzyGrT8jd9Ru34ndL
e0317eb · 2026-09-12 · build-fleet: declare slug aliases + fail the build on an undeclared collision (TK-11255)
DTD verdict B, narrowly scoped. Peer commit 706e430 already fixed the SAND name
constant, so this is the remaining half and deliberately does NOT attempt the
rearchitecture the panel's D voters proposed.

WHAT THE PANEL GOT WRONG, and why this is small: 3 of 5 voted D ('identity does
not belong in a derived artifact') on a premise I supplied incorrectly. The
duplicate slugs are NOT a defect -- slug derives from vid, so THIB/ANNA FRENCH/
ANNA yield three DISTINCT slugs and the generator provably cannot collide them.
Every duplicate row has items:0 and commit ae6499b says 'fleet stubs so
vidContactMap resolves': they are deliberate distributor-alias stubs (Thibaut
distributes Anna French). Deduping them would have BROKEN contact routing.

Three changes:

1. SLUG_ALIASES -- the hand-edited stubs are now an explicit, commented,
   version-controlled whitelist. They previously lived only as hand edits in
   data/fleet.json, so any regeneration would have silently erased them.

2. The alias is applied when DERIVING the slug, not bolted on afterwards. A
   negative test caught that ANNA FRENCH only routed to 'thib' while it had NO
   items; give it real samples and it got its own 'anna-french' slug with no
   contact behind it. Now routing is identical either way, and the aliased vid is
   recorded in aliasVids so stamping/reply-matching don't lose it.

3. A build-time guard at GROUPING time, not on the output rows. The first
   version of this guard PASSED its own injected-fault test for the wrong reason:
    the generator's own `g[slug] = g[slug] || {...}` had already merged the
    colliding vendors into one
   row, so by the time the output was inspected the collision was invisible. That
   is a worse bug than the one I set out to guard -- two distinct vids that
   normalize to one slug silently attribute one vendor's outstanding samples to
   the other. It now refuses to write fleet.json at all.

Tested (positive, alias, and injected fault -- a positive-only test would have
shipped the broken first guard):
  T1 distinct vids            -> writes, no false failure
  T2 ANNA FRENCH WITH items   -> merges into thib, aliasVids recorded, no stray
                                 anna-french slug
  T3 'FOO BAR' + 'FOO-BAR'    -> exit 1, fleet.json NOT written
Run in an isolated tree so the real data/fleet.json was never touched.

Consumer hardening: server.js gains vendorBySlug() and scripts/fmpro.mjs an
inline equivalent, replacing 4 bare .find(x => x.slug === s) calls that depended
on stubs sorting last. Verified behaviour-identical on today's data -- this
removes fragility, it does not fix a live bug.

NOT done on purpose: no runtime join to canonical FileMaker. It is hand-typed,
17% of its sample-email values are not addresses, it became API-readable only
yesterday, and the mailer runs entirely off local files -- that trades a rare
silent-wrong-name for an intermittent cannot-send with nobody on call.

fleet.json is NOT regenerated here; regenerating is now safe but is a separate,
reviewable step.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
706e430 · 2026-09-11 · build-fleet: VID SAND is Sanderson (@ Jasper Showroom), not Sandberg — fix name map so regenerated fleet.json stops mislabeling it (TK-11255)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSLggdzyGrT8jd9Ru34ndL
42be6f7 · 2026-09-11 · contacts.json: mor (Morris & Co) -> Jasper Showroom desk, matching sand; was departed Nicole.Monbarren@ (TK-11255)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSLggdzyGrT8jd9Ru34ndL
641d84c · 2026-09-11 · contacts.json: fix sand — it is Sanderson via Jasper, NOT Sandberg (TK-11255)
This was a live misrouting, not a stale address. The mailer has been chasing
Zoffany and William Morris memo samples at Gimmersta, an unrelated Swedish
company, because contacts.json labelled slug 'sand' as 'Sandberg (via
Gimmersta US)'.

The products prove the identity, which is why this is not a judgement call:
  slug sand  -> ZOW0127-02 / DWZF187058 (Zoffany) and 217209 / DWWM14281
               (William Morris) -- both Sanderson Design Group brands
  slug sandb -> DWSS SKUs -- the REAL Sandberg, left untouched
Canonical FileMaker agreed all along: VID sAND Company = 'Sanderson Wallpaper
@ Jasper Showroom'. contacts.json and fleet.json were both wrong on the name.

Evidence chain for the new address, each link human-authored and sample-topical:
  1. canonical Nicole.Monbarren@sandersondesigngroup.com AUTO-REPLIES 'no longer
     with Sanderson Design Group' to our own 10-day chase (2026-07-07)
  2. Melissa Bacoka (SDG National Sales Manager, West), 2026-07-09: 'you can
     request samples through Jasper, copied here'
  3. Kathalene Barton (Jasper outside sales), twice: 'Copying in our Sample Dept'
  4. Sample@jaspershowroom.com -- Alisa Derghazian, Resource librarian --
     confirms and ships DW sample requests with tracking, most recent 2026-07-09
  5. corroborated by a 2022 SDG email: 'Jasper has Morris, Zoffany & Sanderson'

Note this is a role alias that PASSES the hardened bar on its own evidence
(human-authored, sample-topical), not on the 'desk beats named rep' prior that
this ticket already refuted 3-for-3.

Also corrected the display name, since the wrong label is what caused the
misrouting in the first place.

Undo: restore data/contacts.json.bak-1789163136

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
f195f74 · 2026-09-11 · contacts.json: fix 3 vendor sample desks on Gmail evidence, not preference (TK-11255)
DTD verdict D-hardened. The contrarian's objection was that a naive 'recent
two-way correspondence' test measures whether a human replied to SOMETHING, not
whether the address is the sample-intake channel -- and that it would be fooled
by ticket-system auto-acks on exactly the desk-alias rows in dispute. So the
query required an INBOUND, human-authored, sample-topical reply, ran a positive
control in the same pass, and used sand as a negative control.

Both controls behaved: cathy.dy@thibautdesign.com (control) returned live
human sample replies; support@gimmersta.com (negative control) returned ONLY
machine order-confirmations and produced no qualifying reply. The instrument
discriminates rather than rubber-stamping domain activity.

  mom  customerservice@ -> ddedic@momtex.com
       mined value returned ONLY machine 'SAMPLE SHIPPED NOTIFICATION' mail;
       Diana Dedic answers our sample-followups by hand, most recently TODAY.
  pf   mocampo@ -> hbarragan@kneedlerfauchere.com
       STRONGEST case: on 2026-08-17 mocampo@ replied to our own sample chase
       with 'Please send your sample request directly to our Sample Department,
       as I do not handle sample requests.' We kept mailing her for a month.
       hbarragan@ signs as 'lead sample librarian'.
  hyg  support@ -> info@hyggeandwest.com
       mined value is a MARKETING newsletter sender; info@ answers sample
       requests with a human.

NOT changed -- sand falls to escalation exactly as the verdict required, and
BOTH candidates are now disproved: canonical Nicole.Monbarren@ auto-replies
'no longer with Sanderson Design Group' (forwarding to Melissa.Bacoka@), and
mined support@gimmersta.com is automated receipts only. New lead, not a fix.

Refutes the 'general desk beats named rep' prior the panel nearly adopted as a
tiebreaker: every generic alias here is a bot or a mailing list and every real
sample desk is a named human -- it would have chosen wrong 3 for 3.

Undo: restore data/contacts.json.bak-1789162556

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6d72c91 · 2026-09-11 · contacts.json: adopt canonical sample desks for dtex + mdc (TK-11255)
Steve: 'take your recommendations'. Applied ONLY the 2 drift rows where the
canonical FileMaker value is unambiguously the better one -- a dedicated sample
desk replacing a named rep / general CS address:
  dtex  ddawson@designtex.com -> samples@designtex.com
  mdc   cs@mdcwall.com        -> sample@mdcwall.com

Each write asserted the CURRENT value equalled the expected prior value before
overwriting, so a state change since the reconciliation would abort rather than
silently clobber a value I never inspected.

Deliberately UNCHANGED, per the same recommendations:
  wol  keeps mined samples@wolfgordon.com -- a dedicated sample desk, and
       dw_unified.vendor_contacts independently agrees against canonical's
       customerservice@. Canonical is authoritative but not uniformly better.
  thib keeps cathy.dy@thibautdesign.com -- this was never real drift. fleet.json
       maps slug 'thib' to three vids (THIB / ANNA FRENCH / ANNA); canonical
       Thib=cathy.dy@ MATCHES, and anna=cs@ is a different vendor. Fix the slug,
       not the address.

Still open, needing a real answer rather than a preference:
  mom, pf, hyg  -- named rep vs general desk; whoever is current wins
  sand          -- Sanderson Design Group vs Gimmersta are DIFFERENT COMPANIES;
                   needs a decision about who actually fulfils Sandberg samples

Undo: restore data/contacts.json.bak-1789162028

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
f7cb4cb · 2026-09-11 · contacts.json: fill 7 blank vendor sample desks from canonical FileMaker (TK-11255)
Steve enabled the fmrest extended privilege on the hosted `vendors` file,
clearing a 9-day blocker. Reconciled all 58 slugs against the canonical
five-column vendor table (layout 'Main Menu', 1,197 rows) on an exact-VID join.

Applied ONLY the 7 gap-fills -- every one was previously BLANK, so no working
address is overwritten, and each value was asserted to parse as a real email
before writing:
  eli fol ftr lin poi rad sch

Deliberately NOT applied: the 8 DRIFT rows, which need a per-row human call.
Notably `wol` KEEPS the mined samples@wolfgordon.com over canonical's
customerservice@ -- it is a dedicated sample desk and dw_unified.vendor_contacts
independently agrees, so canonical is not uniformly the better source.

Also not applied: 47 of 280 (17%) canonical samples-values are NOT addresses --
the field is free text holding operator instructions ('***ORDER ONLINE***',
'DO NOT ORDER SAMPLES!!!!!!!!!!', a partner URL, 'CUT Samples FROM BOOKS IN
HOUSE', and one address with a note concatenated). Blanket-copying canonical
would inject non-addresses into the sample-followup mailer.

Resolves 3 of the 4 long-unresolved vendors: FTR=ftg1@futuretextiles.com,
POI=samples@justindavid.com, ULR=confirmed to have NO sample desk (its field
literally reads DO NOT ORDER SAMPLES). 1838 WALLCOVERINGS has no canonical row.

Undo: restore data/contacts.json.bak-1789161339

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
d0bc2fd · 2026-09-10 · TK-11409: sent-poller stops pre-filling the 2nd-request slot on a first stamp
Last hole in this ticket. watch-sent-stamp.mjs wrote BOTH date fields whenever it
detected a real send, but this path only ever makes a FIRST stamp -- records already
carrying FIELD_SENT are excluded via the 'already' set. So it filled the 2nd-request
field on the first chase, and pass 2 then skipped those records forever because the
field was non-empty, silently defeating the tracking Steve asked for today.

Steve's 8/25 'stamp BOTH' was correct when written: the 2nd-request field had no
separate meaning then, so writing it alongside was the only way to populate it.
TK-11409 gave it a distinct meaning, which is what makes the dual-write wrong now.
This serves that intent rather than reversing it, and matches how fmpro.mjs pass 1
was aligned.

Also corrected two stale comments and a log line that reported '2ndReq=<date>' on a
write that was never a 2nd request.

Dry run clean (FM_READONLY=1, no stampable follow-ups in window, nothing written).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
74e43c2 · 2026-09-10 · TK-11409: the static contact list expires instead of blocking forever
data/recently-contacted.json was a PERMANENT blocklist: hand-written, last edited
2026-08-20, 26 addresses, nothing ever expiring -- and it held the biggest accounts
(Kravet, Thibaut, Osborne & Little, York, WallQuest, Sancar, Innovations, MDC,
Maharam, Koroseal, Phillip Jeffries, Wolf Gordon...). So even after the Gmail query
was corrected to match the chase subject, those vendors stayed suppressed from SEND
forever by a snapshot nobody had looked at in three weeks.

This was a THIRD suppression source. The earlier fix narrowed one of them and I never
checked whether another existed -- same failure family as the rest of the ticket: a
record taken once and trusted indefinitely.

- File converted from a bare array to { address: 'YYYY-MM-DD' }, every entry seeded
  with the file mtime (the only honest date available).
- Entries now EXPIRE after RECENT_DAYS.
- A bare-array file is still accepted but treated as expired-unknown, so an old copy
  or a hand-edit cannot resurrect permanent-blocklist behaviour.
- Each run prints how many are live vs expired, so the list can never go quiet again.

Verified: 0 within 14d, 26 expired. Sancar is now correctly sendable (no chase in 14
days -- its recent mail was new sample ORDERS). WallQuest stays correctly suppressed
(real chase sent 09/01).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
de5df55 · 2026-09-10 · TK-11409: stop shipping the internal 'From job:' banner to vendors
George's withSourceFooter() prepends a visible 'From job: node - <timestamp>' block
INSIDE the message body unless the caller passes no_source_tag. Nothing in this pipeline
passed it, so that internal provenance tag reached vendors on every chase -- confirmed
present in the current drafts AND in chases already SENT on 09/01 and 09/04.

Fixed centrally in the transport rather than at each of the four call sites
(scheduled-run draft + send, send-drafts, send-reconciled, server.js one-click send).
Every path through this transport is a vendor-facing letter -- /api/send and /api/drafts
are its only sendable paths -- and a per-caller flag is a step someone must remember,
which is the failure mode this entire ticket documents. An explicit no_source_tag:false
is still respected.

Verified: unit test confirms the flag on both paths and that explicit-false wins; a live
draft came back source='' with a snippet starting at the real first line instead of
'From job: unspecified'.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
42b4eba · 2026-09-10 · TK-11409: rescue the text of two Aug-15 drafts ~4 days from permanent deletion
com.steve.george-drain-old-drafts permanently deletes info@ drafts at 30 days with no
Trash recovery. Both were written 2026-08-15, so deletion is ~2026-09-14. One is real
unfinished correspondence to Kravet asking two specific unanswered questions (delivery
destination on four samples, status of two outstanding ALTHEA items), unsent for 26 days.

Could not extend/send/move them: the Gmail trash+modify tools are on the claude.ai Gmail
connector (session expired) and the classifier blocks the credential read needed to reach
George's draft endpoints. Capturing the text was the only non-destructive action available.

Also records a separate live defect found while reading them: every chase draft AND every
chase already sent carries a visible 'From job: node' provenance banner inside the message
body, so vendors have been receiving it for weeks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
b4d9ec1 · 2026-09-10 · TK-11409: anti-dup keys on the CHASE subject, not the new-order subject (DTD 6/7 verdict B)
The filter matched subject:'New Sample Request' -- a brand-new ORDER, a different
message type that happens to go to the same vendor desk. So a chase was suppressed
because we had placed an order. Our busiest accounts (Kravet, Thibaut, Osborne, York)
get orders almost daily and were therefore effectively un-chaseable; 33 overdue memos
went unchased for weeks.

Now matches the chase letter's own subject. The -subject:'Re:' exclusion is load-bearing,
not cosmetic: verified against 90 days of info@ Sent, a bare subject:'Sample Follow-Up'
also matches humans replying inside a chase thread AND the 'HAPPY LABOR DAY FROM DW! Re:'
auto-responder -- so without it this fix would have traded one over-suppression bug for
a new one. That correction came from the reviewer's dissent, after the panel had voted.

Verified live: 10 matches, 0 Re:/autoresponder false positives, suppressed recipients
40 -> 10.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ed3bc8d · 2026-09-10 · TK-11409: verify the draft-ledger against reality; surface both silent draft-mode outcomes
Third instance of this ticket's failure family: a PROXY trusted without verification.
The draft-ledger recorded 'already drafted' for Osborne & Little, Thibaut and York
while ZERO drafts for them existed in info@ Drafts. 'if (!fresh.length) continue'
then skipped them SILENTLY -- no report line at all -- so those 8 memos were
permanently invisible: never re-drafted, never reported, never chased.

- georgeHasDraft(to) checks whether a chase draft still exists before trusting the
  ledger. On ANY error it returns true (assume present), so a George hiccup can
  never cause a burst of duplicate drafts.
- A stale claim is dropped and the vendor re-drafted.
- Two previously-silent outcomes now appear in the report and console:
  alreadyDrafted (verified present, correctly not duplicated) and staleLedger.

Verified live: 3 re-drafted (OSB/THIB/YOR, 8 SKUs), 9 correctly left alone as
verified-still-present. Nothing sent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
55c8110 · 2026-09-10 · TK-11409: anti-dup filter moves to the send gate only (DTD 5/5 verdict C)
DRAFT now always includes recently-emailed vendors, flagged for review; SEND keeps
the rolling window strictly enforced. A draft sends nothing and leaves no mailbox,
so suppressing it prevented no harm and only hid work -- which is how 33 overdue
memos across 10 vendors stayed invisible until Steve asked. The --include-suppressed
opt-in is why: a safety step that must be REMEMBERED is not a control, and nobody
ever typed it. Flag retained as a no-op so launchd/callers keep working.

Also killed the stale '8/15' label. There was never an 8/15 cutoff -- the rule is a
rolling RECENT_DAYS (14) window recomputed every run, and the hardcoded reason string
misled everyone, including me, into reading it as a one-time event.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1c8e587 · 2026-09-10 · TK-11409: pass 2 requires send-history evidence, and can no longer fail silently
DTD verdict B, amended by the reviewer's dissent.

Root cause of the inversion, now explained: the 2nd-request field only started
being written on 2026-08-25 (32145b7). The 8 rows pass 2 was matching were stamped
08/14-08/15 -- BEFORE that field was ever populated -- so their empty 2nd-request
field meant 'this column did not exist yet', not 'awaiting a 2nd request'.
fmpro-posted.json corroborates (GREEN 7 + VER 1 = the exact 8). DB field state is
not contact history.

- sendEvidence(slug) reads sent.json as the evidence oracle and returns
  {count, addrs, known}.  (no record at all) is deliberately distinct
  from count<2, because conflating them is how a no-op hides.
- planVendor pass 2 refuses without >=2 recorded sends and sets an explicit _skip
  REASON. It never returns a bare empty list.
- plan --pass 2 prints a REFUSED block naming every vendor and why.
- stampVendor returns , so a caller can tell 'refused for lack of evidence'
  from 'nothing left to do'.
- stampFmpro no longer uses stdio:'ignore' -- the root enabler of BOTH bugs in this
  ticket -- and appends to data/fmpro-stamp.log. Still detached and non-blocking, so
  a FileMaker hiccup still cannot fail a send.

Verified read-only against LIVE FileMaker:
  pass 2 before: 8 records, all false positives
  pass 2 after:  0 records, 29 vendors refused with a named reason
  pass 1:        1 record, unchanged (no regression)
  the 5 slugs with a genuine 2nd send (dgd/osb/thi/thib/christian-lacroix-europe)
  pass the gate -- so it does not over-refuse.
No records written.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
173735d · 2026-09-10 · TK-11409: plan --pass 2 previews the resend match set read-only
Lets the 2nd-request query be proven against live FileMaker without writing.
Verified: pass 1 matches 1 record, pass 2 matches 8 — the 8 that a resend
previously skipped silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
f69c9f7 · 2026-09-10 · TK-11409: the 2nd request now updates the WALLPAPER file
The resend called the stamper but wrote nothing, silently. matchPageSku hard-coded
[FIELD_SENT]: '=' (FileMaker '=' matches an EMPTY field) on every send, so on a resend
every SKU was already stamped from request #1, the find returned zero records, and
nothing was written. stampFmpro spawns detached with stdio:'ignore', so the no-op was
indistinguishable from success.

- matchPageSku/planVendor/stampVendor take a  (default 1, so existing callers
  are unchanged). pass 2 finds rows where FIELD_SENT is non-empty ('*') and the
  2nd-request field is still empty.
- pass 1 now writes ONLY 'Date Email Sent to Vendor after 10 Days'. It used to write
  the 2nd-request field at the same time, which left request #2 nowhere to record.
- pass 2 writes ONLY 'Date Sample Request Letter Sent', preserving the 1st-chase date.
- server.js derives isResend from sent.json (the record of fact) rather than b.force,
  so a forced genuine first send stays pass 1.

Not yet exercised against live FileMaker — no records written.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
8b3d62e · 2026-09-04 · TK-10742: George-mined address spot-check (DTD-B safeguard) — 10/11 flags are legit rep-desk routing, 1 glance (maya)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VGW4NWtm13q7fv8u725FyH
57d645d · 2026-09-01 · chore: surface FM stamp failures (no more silent catch), v1.1.0 (session close)
Lint (no linter configured) + refactor (no safe change — code deliberate) passes.
Applied logging-only hardening on the two silent FM-stamp catch sites the review
flagged (scheduled-run SEND path + watch-sent-stamp poller) — zero control-flow
change, just turns a swallowed stamp failure into a visible warning. Minor bump:
session added weekday+auto-widen window, ref/tracking + pattern-name columns,
WIN/CATCHUP/SEARCH_DAYS/MAX_AGE knobs, --include-suppressed, and the poller rewrite.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
35ba693 · 2026-09-01 · sample-followup: env knobs (SEARCH_DAYS/MAX_AGE) to backfill 2nd-request stamps
Lets the poller sweep further back than the default 3-day/60-day window to backfill
follow-ups sent during the period the stamper was broken. Month sweep found only 1
unstamped-still-outstanding item (Morris 216020); WQ FC60808 + O&P OP-ART-DECO-WAVES
were already received. Backfilled 216020 with its real chase date (08/27).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5ab8a3d · 2026-09-01 · sample-followup: FIX 2nd-request stamping — was silently stamping nothing
The Sent-poller matched the subject account # against FileMaker's account field,
but that field is a DW-internal sequence # (1062xxx), not the vendor account — so
6/25 processed sends stamped ZERO records while the ledger filled up (looked healthy,
did nothing). Rewrote matching: recipient email -> candidate vids (all slug variants,
not last-wins) -> stamp ONLY records whose Manufacturer # actually appears in the SENT
letter body. Body-presence is precise + immune to shared-desk aliasing (Christian
Lacroix via Osborne desk, Anna French via Thibaut desk stamp only if truly in the
letter). Also switched the search phrase to the no-hyphen 'Outstanding Memos' (less
flaky than 'Sample Follow-Up'). Stamps Date Sample Request Letter Sent +
Date Email Sent to Vendor after 10 Days (no dedicated 2nd-Request field exists via API).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
9a219d8 · 2026-09-01 · sample-followup: drop the [REVIEW] subject prefix — flag lives in the run report only
Steve 9/01: the '[REVIEW — team emailed recently]' tag must never ride in the draft
subject (risk of reaching the vendor). Removed it from the subject; the recently-emailed
flag now shows ONLY in the console run report. Deleted + regenerated the 8 scoped drafts
with clean subjects.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

File tree

107 files tracked. Click any to browse the source at HEAD.

Other build journals

← Sample Box  ·  all 4 projects  ·  Sanderson Onboard →

Export

commits.csv · feed.atom · project.json · commits.json

rendered in 9ms