fix: repair decayBeliefs CTE so belief decay actually runs
The CTE was `WITH halflives AS (SELECT profile, CASE profile ...)` with no FROM clause. Postgres rejects it with 42703 (column "profile" does not exist) on every hourly invocation, so the temporal dimension of the recommendation engine had never executed once — obsession (14d half-life) and contextual (7d) never faded. Rewritten as `WITH halflives(profile, halflife_sec) AS (VALUES ...)`, half-lives preserved exactly. One deliberate semantic change: the broken CASE had an `ELSE 30 * 86400` fallback, so an unrecognised profile would have decayed on a 30-day half-life. The VALUES join leaves unknown profiles undecayed instead. Today that is a no-op (only `forgotten`, already excluded by the WHERE), but a future profile added without a half-life will now conspicuously not decay rather than quietly decaying at an arbitrary rate. Verified against a scratch PG16 with one belief per profile aged exactly one half-life: UPDATE 2, obsession and contextual halved, forgotten and a fresh longterm untouched. Against the live DB (in a rolled-back transaction) the fix reports UPDATE 843. NOTE ON ROLLOUT: the first successful run applies ~23 days of accrued decay at once, cutting obsession beliefs to ~0.32x. That is correct behaviour, but recommendations will shift visibly. Expected, not a regression. REVIEW-2026-07-30.md finding 3. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1790,16 +1790,16 @@ export class DbService {
|
||||
*/
|
||||
async decayBeliefs(): Promise<number> {
|
||||
const res = await this.pgClient.query(`
|
||||
WITH halflives AS (
|
||||
SELECT profile,
|
||||
CASE profile
|
||||
WHEN 'longterm' THEN 365 * 86400
|
||||
WHEN 'obsession' THEN 14 * 86400
|
||||
WHEN 'discovery' THEN 30 * 86400
|
||||
WHEN 'negative' THEN 180 * 86400
|
||||
WHEN 'contextual' THEN 7 * 86400
|
||||
ELSE 30 * 86400
|
||||
END AS halflife_sec
|
||||
-- NOTE: this MUST be a VALUES list, not "SELECT profile, CASE profile ...",
|
||||
-- which has no FROM clause and is rejected by Postgres with 42703
|
||||
-- (column "profile" does not exist) on every single run.
|
||||
WITH halflives(profile, halflife_sec) AS (
|
||||
VALUES
|
||||
('longterm', 365 * 86400),
|
||||
('obsession', 14 * 86400),
|
||||
('discovery', 30 * 86400),
|
||||
('negative', 180 * 86400),
|
||||
('contextual', 7 * 86400)
|
||||
)
|
||||
UPDATE listener_beliefs lb
|
||||
SET value = GREATEST(-1.0, LEAST(1.0, lb.value * POWER(0.5,
|
||||
|
||||
Reference in New Issue
Block a user