Phase 1.3 — signing-invitation role copy
- Order-agnostic phrasing (was assuming client→developer→approver order;
ports configure any sequence so the "client has already signed"
assumption was brittle).
- Explicit developer-role branch + safe default for unknown roles.
Phase 1.4 — supplemental form per-port URL
- New supplemental_form_url registry entry (email.from section).
- Threaded through getPortEmailConfig → PortEmailConfig.supplementalFormUrl.
- /api/v1/interests/[id]/supplemental-info-request resolves the link
via per-port URL when set, falls back to /public/supplemental-info/<token>
CRM route when blank.
Phase 2 — deal-pulse signal expansion + admin config
- Compute function gains:
- +5 eoi_sent_recent (≤14d) — was previously invisible
- +15 deposit_received — strongest near-commit signal
- +10 contract_signed — closed-loop reinforcement until outcome flips
- -25 document_declined — strongest cooling signal
- -20 reservation_cancelled — booked-then-cancelled warning
- -30 berth_sold_to_other — primary berth lost to another deal
- Each signal honours optional per-port `signal_<id>_enabled` toggle.
- Registry adds master toggle (pulse_enabled), per-signal toggles, and
per-port label overrides (Hot/Warm/Cold rename).
- New /admin/pulse page mounted via RegistryDrivenForm.
- AdminSectionsBrowser entry under Configuration.
Data-wiring for the 3 risk signals (declined/cancelled/sold-to-other)
needs follow-up: requires either schema timestamps on interests or
derivation from event tables. Master plan §B captures the gap.
Tests: 1374/1374 passing. tsc clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>