[object Object]

← back to Ken

feat(ken): WEATHER_PULSE_SHADOW mode — log the nudge, do not apply to p_yes

67b0c90719cbf4ad8680567cc5eb5785223d09d8 · 2026-08-13 13:23:34 -0700 · Steve Abrams

Money-safe: with WEATHER_PULSE_SHADOW=1 the pulse is computed + logged into
components.weather_pulse but p_yes is unchanged (verified pYesFinal===pYes), so real
trades cannot move. ecosystem bertha-cron env set to WEATHER_PULSE=1 + SHADOW=1 for
live-market validation before the nudge is ever allowed to influence a real order.

Files touched

Diff

commit 67b0c90719cbf4ad8680567cc5eb5785223d09d8
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Aug 13 13:23:34 2026 -0700

    feat(ken): WEATHER_PULSE_SHADOW mode — log the nudge, do not apply to p_yes
    
    Money-safe: with WEATHER_PULSE_SHADOW=1 the pulse is computed + logged into
    components.weather_pulse but p_yes is unchanged (verified pYesFinal===pYes), so real
    trades cannot move. ecosystem bertha-cron env set to WEATHER_PULSE=1 + SHADOW=1 for
    live-market validation before the nudge is ever allowed to influence a real order.
---
 ecosystem.config.js | 3 ++-
 src/lib/model.js    | 9 +++++++--
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/ecosystem.config.js b/ecosystem.config.js
index 9759681..53863c9 100644
--- a/ecosystem.config.js
+++ b/ecosystem.config.js
@@ -19,7 +19,8 @@ module.exports = {
       cwd: '/root/Projects/Bertha',
       env: {
         NODE_ENV: 'production',
-        WEATHER_PULSE: '1',              // enable the free NWS-AFD weather-news pulse (advisory, capped ±0.03, contested-only)
+        WEATHER_PULSE: '1',              // compute the free NWS-AFD weather-news pulse (advisory, capped ±0.03, contested-only)
+        WEATHER_PULSE_SHADOW: '1',       // SHADOW: log the nudge but DO NOT apply it to p_yes (zero effect on real trades)
         // WEATHER_PULSE_CAP: '0.03',    // optional: max |nudge| (default 0.03)
         // WEATHER_PULSE_CONTEST: '0.10',// optional: |p-0.5| band counted as contested (default 0.10)
       },
diff --git a/src/lib/model.js b/src/lib/model.js
index 7891028..38c893a 100644
--- a/src/lib/model.js
+++ b/src/lib/model.js
@@ -111,7 +111,12 @@ async function generatePrediction({ conditionId, forecastFeatures, eventSpec, mo
     const { weatherPulse } = require('./weather-pulse');
     pulse = await weatherPulse({ pYes, variable, office: eventSpec?.office, location: eventSpec?.location });
   } catch { /* fail-safe: no nudge */ }
-  const pYesFinal = Math.max(0.01, Math.min(0.99, pYes + (pulse.nudge || 0)));
+  // SHADOW mode (WEATHER_PULSE_SHADOW=1): compute + LOG the nudge but DO NOT apply it to p_yes.
+  // => provably zero effect on recommendations / real trades — used to validate the signal on
+  // live markets before it is ever allowed to influence a real order.
+  const pulseShadow = process.env.WEATHER_PULSE_SHADOW === '1';
+  const appliedNudge = pulseShadow ? 0 : (pulse.nudge || 0);
+  const pYesFinal = Math.max(0.01, Math.min(0.99, pYes + appliedNudge));
 
   // Step 5: Confidence based on data quality
   const dataPoints = forecastFeatures?.values?.length || 0;
@@ -126,7 +131,7 @@ async function generatePrediction({ conditionId, forecastFeatures, eventSpec, mo
       climatological_prior: prior,
       bayesian_combined: combined,
       data_points: dataPoints,
-      weather_pulse: pulse,          // { nudge, reason, score? } — advisory, capped
+      weather_pulse: { ...pulse, shadow: pulseShadow, applied: appliedNudge }, // shadow=log-only (no p_yes change)
     }
   };
 }

← a6239c3 chore(ken): enable WEATHER_PULSE=1 in bertha-cron env (advis  ·  back to Ken  ·  chore(ken): v1.1.0 (session close — weather-news pulse featu 75b875f →