diff --git a/.specs/features/mass-mailer/tasks.md b/.specs/features/mass-mailer/tasks.md index dc942cc..c823591 100644 --- a/.specs/features/mass-mailer/tasks.md +++ b/.specs/features/mass-mailer/tasks.md @@ -8,10 +8,10 @@ | F02 ✅ | MDB initializer with versioned schema | F01 | Fresh MDB, tables, unique indexes, and schema version 1 created in disposable integration test. | | F03 ✅ | Bitness-aware launcher and real MDB preflight | F01, F02 | Valid x86 path passes; missing MDB, x64 provider mismatch, invalid interval, and schema 2 fail. | | F04 ✅ | HTA status shell | F03 | Hidden-window smoke and scripted status query passed; no campaign send process exists. | -| F05 ◐ | SMTP transport and protected secret feasibility | F02 | STARTTLS 1.3, DPAPI round trip/rotation, and inline-image pickup MIME pass; authenticated internal send and inbox check pending. | +| F05 ✅ | SMTP transport and protected secret feasibility | F02 | STARTTLS 1.3, DPAPI round trip/rotation, and inline-image pickup MIME pass; owner reports the authenticated internal SMTP test worked. Detailed inbox image rendering is not separately recorded. | | F06 ✅ | Contact import and suppression controls | F02, F04 | Disposable MDB integration test passes import, duplicate rejection, re-import rejection, and suppression. | | F07 ✅ | HTML template import and safe preview | F02, F04 | Disposable MDB test passes image snapshot and preview after deleting source; unsafe paths, active content, and CSS image URLs fail. Preview does not yet include campaign footer. | -| F08 | Campaign approval and test send | F05-F07 | Snapshot recipient/content/identity; explicit approval and internal-only test send. | +| F08 ◐ | Campaign approval and test send | F05-F07 | Disposable MDB test passes full footer preview, suppression-sensitive review token, internal-only MIME, and paused approval. Real postal address and campaign-specific inbox test pending. | | F09 | In-app dispatch, pacing, pause, and recovery | F08 | Fake adapter tests cover gap, close/reopen, suppression, and uncertain outcomes. | | F10 | Reporting, backup, and pilot readiness | F09 | Export, restore drill, and manual pilot checklist pass. | diff --git a/.specs/features/mass-mailer/test-plan.md b/.specs/features/mass-mailer/test-plan.md index 58a79a4..9c7f7f7 100644 --- a/.specs/features/mass-mailer/test-plan.md +++ b/.specs/features/mass-mailer/test-plan.md @@ -45,6 +45,8 @@ ## Release gates +On 2026-10-22, the owner reported that password entry and the one-message authenticated SMTP transport test worked. The specific received-image rendering result was not captured. The campaign-specific test and live dispatch gates remain open. + 1. All P1 stories and T01-T32 applicable to the chosen provider pass. 2. Sender domain and provider limits verified; the system can display provider rejection and halt safely. 3. Unsubscribe route is tested end to end; a recorded opt-out takes effect before the next dispatch. Any manual mailbox procedure has an owner and a tested processing schedule. diff --git a/.specs/project/STATE.md b/.specs/project/STATE.md index 098ccc4..d97404d 100644 --- a/.specs/project/STATE.md +++ b/.specs/project/STATE.md @@ -2,7 +2,7 @@ ## Current milestone -F01-F04, F06, and F07 are implemented and locally verified. F07 imports constrained well-formed HTML and local image snapshots, with an isolated stored-content preview; the campaign footer belongs to F08. F05 has a successful unauthenticated STARTTLS 1.3 handshake, protected password storage tests, and an inline-image MIME pickup test. The authenticated internal send and inbox rendering are pending local password setup. Campaign sending remains unavailable. +F01-F07 are implemented and locally verified. The owner reports the authenticated internal SMTP test worked after entering the password; detailed received-image rendering is not separately recorded. F08 now has review, full footer preview, campaign-specific internal test, and paused approval. Its disposable MDB/MIME integration test passes; the real campaign-specific inbox test requires the physical postal address. Campaign sending remains unavailable. ## Decisions @@ -14,6 +14,6 @@ F01-F04, F06, and F07 are implemented and locally verified. F07 imports constrai ## Next dependencies -- Enter the SMTP password locally with the HTA setup action, then run its one-message internal test and inspect the received HTML/CID image. -- Enter the business postal address in local `config.ini` before campaign approval. -- Implement campaign approval and in-app dispatch (F08-F09), including a full message preview with the required footer. +- Enter the business postal address in local `config.ini` before campaign review and approval. +- Import the intended template, run its campaign-specific internal test, and inspect the received message before approving it. +- Implement in-app dispatch, pacing, pause, recovery, and reporting (F09-F10) before any business-contact send. diff --git a/MassMailer.hta b/MassMailer.hta index df7c5e6..d996433 100644 --- a/MassMailer.hta +++ b/MassMailer.hta @@ -27,7 +27,7 @@ .bad { color: #9c2929; } .good { color: #235e37; } #error { color: #9c2929; white-space: pre-wrap; margin-top: 12px; } - #templatePreview { width: 100%; height: 440px; border: 1px solid #dbe1e7; background: white; margin-top: 12px; } + #templatePreview, #campaignPreview { width: 100%; height: 440px; border: 1px solid #dbe1e7; background: white; margin-top: 12px; }
@@ -400,6 +525,21 @@ +Approval captures currently eligible contacts and the selected template version. It requires a complete postal address in config.ini, a campaign-specific internal test, and your confirmation that you received it. Approved campaigns stay paused until dispatch controls are built.
+ + + + + + + + + + +| ID | Campaign | Status |
|---|
Add unsubscribe replies here before the next send. Suppression applies across all campaigns.
diff --git a/README.md b/README.md index 89e8f8d..9948a2f 100644 --- a/README.md +++ b/README.md @@ -10,23 +10,25 @@ Windows HTA application under construction for individual marketing emails for a - Imports constrained, well-formed HTML templates from `TEMPLATE_ROOT`, snapshots local PNG/JPEG/GIF images into the MDB, and previews the stored version in an untrusted HTA frame. - Stores the SMTP password as a Windows user-protected value in the MDB through an interactive local setup action. - Offers an explicit, single-message SMTP transport test to the configured `SMTP_TEST_TO` address. Campaign sending is not implemented yet. +- Reviews a campaign against unsuppressed contacts, adds an advertisement, postal, and reply opt-out footer, tests the reviewed message only to the internal inbox, and records approval in a paused state. The app sends no marketing messages in this build. Closing the HTA stops all in-app activity. ## Setup on this PC -1. Copy `config.example.ini` to `config.ini` and edit local settings. Set `SMTP_TEST_TO` to the owner-controlled test inbox. `BUSINESS_POSTAL_ADDRESS` remains blank until you supply it; future campaign approval will require it. Never place the SMTP password in the INI. +1. Copy `config.example.ini` to `config.ini` and edit local settings. Set `SMTP_TEST_TO` to the owner-controlled test inbox. `BUSINESS_POSTAL_ADDRESS` remains blank until you supply it; campaign review and approval require it. Never place the SMTP password in the INI. 2. Run `C:\Windows\SysWOW64\cscript.exe //nologo tools\init-db.vbs` once to create `data\MassMailer.mdb`. It refuses to overwrite an existing database. 3. Run `Start-MassMailer.cmd`. The launcher selects the configured script architecture and opens the MDB before starting the HTA. Use `C:\Windows\SysWOW64\cscript.exe //nologo tools\launch.vbs check` to check startup without opening the UI. 4. Import a UTF-8 CSV with an `email` header. Optional columns are `business_name`, `source_reference`, and `acquired_at` (`YYYY-MM-DD`). Supply a source reference in the CSV or the form; preview and review rejected rows before importing. -5. Enter opt-out replies in the suppression form. To prepare an internal transport test, press **Set or rotate SMTP password** and enter the password in the local console. Then press **Send one internal test** in the HTA. The test sends one HTML message with an inline image to `SMTP_TEST_TO`; no live test has been sent from this project yet. -6. Put template files under `TEMPLATE_ROOT` and import them from the HTA. The accepted HTML subset and example are in [templates/README.md](templates/README.md). Imported versions are immutable snapshots; the preview currently shows the template content only. Campaign footer and approval are still in development. +5. Enter opt-out replies in the suppression form. Press **Set or rotate SMTP password** to enter the password locally, then press **Send one internal test**. The owner reports that this authenticated SMTP test worked on this PC; detailed received-image rendering has not been separately recorded. +6. Put template files under `TEMPLATE_ROOT` and import them from the HTA. The accepted HTML subset and example are in [templates/README.md](templates/README.md). Imported versions are immutable snapshots. The template preview shows stored content, while the campaign preview adds the full footer. +7. Fill in `BUSINESS_POSTAL_ADDRESS` in local `config.ini`. Enter a campaign name and subject, select an imported template, and press **Review message and recipients**. After checking the full preview, send the campaign-specific test to `SMTP_TEST_TO` and confirm it arrived. Approval records paused deliveries; it does not start sending. Local `config.ini`, MDB files, logs, backups, and temporary test data are ignored by Git. ## Verification -Run `powershell.exe -NoProfile -ExecutionPolicy Bypass -File tools\test-contact-flow.ps1` for a disposable MDB integration check. It covers database initialization and preflight, protected password save/load/rotation using synthetic values, local MIME pickup with an inline image, contact import, duplicate rejection, and suppression. Run `powershell.exe -NoProfile -ExecutionPolicy Bypass -File tools\test-template-flow.ps1` for template import, snapshot, preview, and unsafe-input checks. `C:\Windows\SysWOW64\cscript.exe //nologo //E:JScript tools\test-contacts.js` covers CSV parsing and validation. `powershell.exe -NoProfile -ExecutionPolicy Bypass -File tools\test-smtp-capabilities.ps1` checks the configured server's STARTTLS handshake without authenticating or sending mail. +Run `powershell.exe -NoProfile -ExecutionPolicy Bypass -File tools\test-contact-flow.ps1` for a disposable MDB integration check. It covers database initialization and preflight, protected password save/load/rotation using synthetic values, local MIME pickup with an inline image, contact import, duplicate rejection, and suppression. Run `powershell.exe -NoProfile -ExecutionPolicy Bypass -File tools\test-template-flow.ps1` for template import, snapshot, preview, and unsafe-input checks. Run `powershell.exe -NoProfile -ExecutionPolicy Bypass -File tools\test-campaign-flow.ps1` for recipient review, stale-review rejection, internal-only campaign MIME, and paused approval. `C:\Windows\SysWOW64\cscript.exe //nologo //E:JScript tools\test-contacts.js` covers CSV parsing and validation. `powershell.exe -NoProfile -ExecutionPolicy Bypass -File tools\test-hta-syntax.ps1` checks the HTA script. `powershell.exe -NoProfile -ExecutionPolicy Bypass -File tools\test-smtp-capabilities.ps1` checks the configured server's STARTTLS handshake without authenticating or sending mail. ## Planning documents @@ -37,4 +39,4 @@ Run `powershell.exe -NoProfile -ExecutionPolicy Bypass -File tools\test-contact- - [Example SMTP configuration without a password](config.example.ini) - [Access provider probe for 32-bit and 64-bit hosts](tools/probe-mdb-providers.vbs) -System-wide settings belong in the INI; campaign settings, templates, contacts, queue state, and protected secrets belong in the Access MDB. Authenticated SMTP and inbox rendering still need an internal test message. +System-wide settings belong in the INI; campaign settings, templates, contacts, queue state, and protected secrets belong in the Access MDB. Live campaign dispatch, pacing, recovery, and reporting are still under development. diff --git a/templates/README.md b/templates/README.md index aa2d8ee..fb1334c 100644 --- a/templates/README.md +++ b/templates/README.md @@ -14,4 +14,4 @@ Example: