# Broadcast draft name and manual contact list — locally fixed and verified

Date: 2026-09-19. Both application defects are complete under the local verification scope. No production requests, customer records, browser sessions, sends, provider calls, jobs or deployments were used.

## Source change

Only `websitemint/packages/ui/src/components/crm/broadcast/broadcast-creator.tsx` changed.

- Both draft and send payload builders now include the internal Broadcast Name in `data.name`. `RepositoryCrudService.create` derives the persisted envelope name from `data.name || data.title || data.email`, so supplying only the envelope name lost it on first creation. The editor also reads a nested name when an older record has no envelope name. Blank drafts retain `Untitled Broadcast`.
- Manual contact creation now supplies `username: email` and `accountType: 'contact'`, matching the established contact-import contract. The actual repository customer-create gate permits this contact marker; ordinary customer-account creation still requires the user API. No password or account-registration flow is invoked.
- Addresses are trimmed, normalized and deduplicated. A refreshed exact-email lookup reuses an existing contact without modifying it; ambiguous matches stop with an error. This also lets retries reuse contacts persisted before a later failure.
- A static contact group receives the actual saved contact `sk` values and their count. Contact failures, malformed/missing save acknowledgments and group failures remain visible; the form retains its inputs and never announces a zero-contact success. Pending saves disable input edits, repeat submission and dismiss. Success appears only after the group returns its persisted identifier.

## Reproducible verification

From the docs checkout:

```sh
node tutorials-plan/application-fixes/assets/broadcast-draft-and-contact-list/verify.cjs
```

Optional arguments are the websitemint and appengine checkout paths. Existing checkout dependencies are used; nothing is installed. Output: [verification.txt](assets/broadcast-draft-and-contact-list/verification.txt). Fixture: [verify.cjs](assets/broadcast-draft-and-contact-list/verify.cjs).

**22 checks passed.** The fixture transpiles and mounts the actual BroadcastCreator and ManualEmailEntry React components, executes actual BaseService creation methods, then the actual repository CRUD create and Mongo provider create methods. Database insert/find, transport, schema hooks, events and commands are isolated; send/command services and real fetch fail if called. Updating an existing draft uses an explicit fixture update boundary. No full application build is claimed.

Coverage includes first draft persistence and reopening, subsequent rename, blank/nested names, normalized unique contact membership, existing-customer reuse without mutation, partial-contact and group-failure retries, ambiguous lookup, failed/null contact/group responses, malformed/failed lookup, retained inputs and disabled controls while saving. Negative controls remove the new canonical name and contact marker: the actual backend reproduces the missing draft name and rejects customer creation, respectively. The send payload's canonical name is checked statically; no send action executes.

`git diff --check` passed for the source change. The course and central review/index files were not edited.

## Boundaries

The fixture establishes the local component/service contracts; it does not exercise HTTP authentication, a real Mongo database, schema/index setup, workflow listeners or message delivery. Contact and group writes remain separate operations: successfully created contacts survive a later group failure and are reused on retry. A lost group-save acknowledgment cannot prove whether a group was persisted; the UI reports failure rather than claiming success. This patch does not add distributed transactions or group-creation idempotency.
