Plan §4.4 + §13: pure SQL recommender, no AI. Single CTE chain
(feasible -> aggregates) + JS-side tier classification, fall-through
cooldown filter, heat scoring, and fit ranking. Per-port settings via
system_settings layered over global + DEFAULT_RECOMMENDER_SETTINGS.
Tier ladder (default):
A : no interest history
B : lost-only history (still recommendable + boosted by heat)
C : active interest in early stage (open..eoi_signed)
D : active interest at deposit_10pct or beyond (hidden by default)
Heat (only for tier B):
recency weight 30 full @ <=30 days, decays to 0 @ 365 days
furthest stage weight 40 full when prior reached deposit
interest count weight 15 saturates at 5+
EOI count weight 15 saturates at 3+
Multi-port isolation enforced (§14.10 critical): the SQL filters by
port_id AND the entry-point function rejects cross-port interest
lookups with an explicit error. Fall-through policy supports
immediate_with_heat (default), cooldown, and never_auto_recommend.
15 unit tests covering tier classification, heat saturation, weight
tuning, zero-weight guard. Smoke-tested end-to-end via
scripts/dev-recommender-smoke.ts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>