← back to Sample Followup Sweep

README.md

58 lines

# Sample Follow-Up Sweep

DW ↔ vendor sample-chase automation. For every vendor with memo samples ordered
>10 days ago and not yet received, compose **one** follow-up letter → draft it to
`info@` → human batch-sends → stamp `2nd Request = today` on the chased rows.

Canary vendor: **Designers Guild @ Osborne And Little** (O&L USA, acct 1433803).

## Pipeline

| Phase | What | Status here |
|-------|------|-------------|
| 1 Sweep | filter a vendor's requested rows: `today − Entered > 10d`, not disco, not received | ✅ `lib/sweep.js` (runs off a seed; swap for live `fm_find` once `API_SampleMemos` exists) |
| 2 Resolve | recipient = vendor's **Email address for Samples** (skip + report if missing) | ✅ `bin/run.js` |
| 3 Compose | verbatim letter template, one line per overdue Mfr# | ✅ `lib/compose.js` |
| 4 Draft | emit `gmail_create_draft` payload + HTML preview (never auto-sends) | ✅ `out/*.draft.json` |
| 5 Send | **human gate** — you batch-send from info@ Drafts | manual |
| 6 Stamp | `2nd Request = today` via `fm_update_record` dry-run→commit | needs `API_SampleMemos` layout (else push the FileMaker button) |

## Fail-closed pre-send compliance gate

Every automated draft/send path and every `gmail_create_draft` artifact routes through
`lib/george-transport.js`, which calls `lib/pre-send-gate.js` before any network request. It
blocks unless the sender resolves to `info@designerwallcoverings.com`, the body
contains DW's physical postal address and a working opt-out link, and the subject
is present without a misleading `Re:`/`Fwd:` prefix. A block performs no network
request and returns `PRE_SEND_COMPLIANCE_BLOCKED` (HTTP 422 in the console API).
Address text and a meaningful opt-out anchor label must survive an HTML5 parsed-tree
walk. Comments, malformed script markup, non-rendered elements (`script`, `style`,
`template`, `noscript`), and elements hidden through HTML attributes or supported
inline styles never count. This is not browser layout or a computed-stylesheet engine;
class-based CSS hiding is outside the trusted-template gate and remains a hardening limit.
The current approved opt-out is the `info@designerwallcoverings.com` mailto link.
HTTPS opt-outs fail closed until a canonical DW-owned endpoint is explicitly registered.

## The one FileMaker dependency (for hands-free phases 1 & 6)

Create a Data-API-enabled list layout on **`WALLPAPER2`** named `API_SampleMemos`
carrying: `Mfr Pattern`, `combo sku`(DW#), `vid`, `account`, `Requested/Again`,
`Entered`, `Date WP Sample Sent`, `Date Discontinued`, `2nd Request`,
`2nd Request Notes`. The vendor dashboard (`Main Menu Copy9`) is **not** Data-API
accessible and the connector cannot run FileMaker scripts, so this thin layout is
the read/write bridge.

## Run

```sh
node bin/run.js osborne-little
open out/osborne-little.preview.html
```

## Decisions (config per vendor in the seed's `sweep_config`)

- **A** rows already carrying a 2nd Request → `secondRequestPolicy`: `escalate` (fleet default) | `include` (canary matches the letter Steve sent)
- **B** disco rows → `excludeDisco: true`
- **C** send mode → draft only (v1); no auto-send
- **D** cadence → daily, idempotent (a row stamped today is skipped)