diff --git a/MASTER_BACKLOG.md b/MASTER_BACKLOG.md index 45e5b582..2fcb55a8 100644 --- a/MASTER_BACKLOG.md +++ b/MASTER_BACKLOG.md @@ -37,7 +37,7 @@ | **L9 Commerce** | Billing Reconciliation | **L1 Physics / L4 Market** | Inaccurate split-billing or tariff logic | ✅ Active | | **L4 Market Gateway** | Capacity Cache | **L3 VPP Aggregator** | Bidding latency exceeds 50ms ISO SLA | ✅ v3.3.1 Active | | **L4 Market Gateway** | Confidence Fallback | **L2 Grid Signal (v2.5.0)** | Missing high-fidelity metadata for L11 | ✅ Active | -| **L10 Token Engine** | Engagement Triggers | **L6 Engagement Engine (v5.14.0)** | Rewards fail for 'ISO Explorer' challenges | ✅ Sync | +| **L10 Token Engine** | Engagement Triggers | **L6 Engagement Engine (v5.15.0)** | Rewards fail for 'ISO Explorer' challenges | ✅ Sync | | **L2 Grid Signal** | Regional Pricing | **L4 Market Gateway (v3.8.4)** | VTN cannot see market-aware grid signals | ✅ Sync | | **L11 ML Engine** | Sentinel Audit | **L10 Token Engine (v4.3.5)** | Phase 6 AI auditing lacks ground truth | ✅ Active | @@ -87,8 +87,10 @@ - [✓] **Standardized Metrics**: Enforced string formatting (`.toFixed(4)`) for all scores. - [~] **BESS RL Bidding**: Research phase for reinforcement learning models (10%). -### Layer 6: Engagement Engine (v5.14.0) +### Layer 6: Engagement Engine (v5.15.0) - [✓] **Solar Surge**: Achievement for CAISO solar ramp response tracking. +- [✓] **Solar Flare**: Achievement for 25 cumulative solar ramp responses (Phase 6 Alignment). +- [✓] **Robust Site ID**: Hardened handleGridSignal with multi-key site identification. - [✓] **Sustainability Refinement**: Optimized recursive CTE for consecutive charging streaks. - [✓] **ISO Explorer**: Multi-regional achievement logic using bulk CTE/UNION. - [✓] **Energy Architect**: Achievement for AI Readiness and historical data contribution. diff --git a/PLATFORM_STATUS.md b/PLATFORM_STATUS.md index 07b86273..29ce7b01 100644 --- a/PLATFORM_STATUS.md +++ b/PLATFORM_STATUS.md @@ -813,7 +813,7 @@ done | **L3** | VPP Aggregator | `3.3.1` | ✅ Operational | | **L4** | Market Gateway | `3.8.5` | ✅ Operational | | **L5** | Driver Experience API | `4.1.0` | ✅ Operational | -| **L6** | Engagement Engine | `5.12.0` | ✅ Operational | +| **L6** | Engagement Engine | `5.15.0` | ✅ Operational | | **L7** | Device Gateway | `5.8.0` | ✅ Operational | | **L8** | Energy Manager | `2.1.0` | ✅ Operational | | **L9** | Commerce Engine | `5.1.0` | ✅ Operational | @@ -825,7 +825,7 @@ done ## Latest Release Wins (April 2026) - **L10 Token Engine (v4.3.3)**: Implemented **Sentinel Fidelity** logic (physics_score > 0.99) and site-aware auditing. Hardened Kafka consumer with robust float parsing and boolean casting. -- **L6 Engagement Engine (v5.12.0)**: Deployed **Physics Sentinel** and **L11 Data Guardian** achievements. Integrated site_id extraction from L7/L10 Kafka payloads for localized engagement. +- **L6 Engagement Engine (v5.15.0)**: Deployed **Solar Flare** achievement and hardened **handleGridSignal** with robust multi-key site identification (`site_id`, `siteId`, `location_id`, `locationId`) for multi-site parity. - **L4 Market Gateway (v3.8.2)**: Exposed `/data/training/fuel-mix` and `/data/training/load-forecast` endpoints for L11 ML readiness. Hardened high-fidelity synchronization with L1/L2 fallbacks. - **L2 Grid Signal (v2.4.8)**: Secured `/openadr/v3/reports` with PII masking and `authenticateToken` middleware. Implemented proactive signal caching for `ADVANCE_CHARGE_SIGNAL` (CAISO solar ramp). - **L7 Device Gateway (v5.8.0)**: Hardened **Sentinel Fidelity** logic and string-formatted scores (.toFixed(4)) for L11 parity. Integrated **helmet()** middleware and enhanced ISO 15118 certificate validation. Advanced OCPI 2.2 mapping to 75%. diff --git a/package-lock.json b/package-lock.json index bebbff88..1ffc914f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19416,7 +19416,7 @@ }, "services/02-grid-signal": { "name": "grid-signal", - "version": "2.5.0", + "version": "2.5.1", "dependencies": { "ajv": "^8.12.0", "dotenv": "^16.3.1", @@ -19551,7 +19551,7 @@ }, "services/04-market-gateway": { "name": "@migrid/market-gateway", - "version": "3.8.4", + "version": "3.8.5", "license": "Apache-2.0", "dependencies": { "axios": "^1.6.0", @@ -19585,7 +19585,7 @@ }, "services/06-engagement-engine": { "name": "@migrid/engagement-engine", - "version": "5.13.0", + "version": "5.15.0", "license": "Apache-2.0", "dependencies": { "express": "^4.18.0", @@ -19603,7 +19603,7 @@ }, "services/07-device-gateway": { "name": "device-gateway", - "version": "5.7.0", + "version": "5.8.0", "dependencies": { "ajv": "^8.12.0", "ajv-formats": "^3.0.1", @@ -19681,7 +19681,7 @@ }, "services/10-token-engine": { "name": "@migrid/token-engine", - "version": "4.3.4", + "version": "4.3.5", "dependencies": { "axios": "^1.6.0", "decimal.js": "^10.4.3", diff --git a/services/06-engagement-engine/WEEKLY_REPORT_APRIL_2026_V5_15_0.md b/services/06-engagement-engine/WEEKLY_REPORT_APRIL_2026_V5_15_0.md new file mode 100644 index 00000000..76fb023e --- /dev/null +++ b/services/06-engagement-engine/WEEKLY_REPORT_APRIL_2026_V5_15_0.md @@ -0,0 +1,22 @@ +# Weekly Product Update: L6 Engagement Engine (v5.15.0) +## Date: April 2026 + +### L6 Gamification & Dependency Report +This week focused on **Standardized Site Identification & Advanced Solar Alignment**. As the platform matures toward Phase 6 AI & Optimization, the Engagement Engine has been hardened to ensure consistent multi-site tracking and deeper integration with L4's market-aware grid signals. + +* **L2 Grid Signal v2.5.1 Alignment:** L6 now adopts the robust multi-key site identification pattern (`site_id`, `siteId`, `location_id`, `locationId`), ensuring that grid signals targeting specific locations are accurately mapped to driver actions. +* **L4 Market Gateway v3.8.5 Sync:** With L4's advanced Solar Ramp detection now fully operational, L6 has introduced a higher-tier achievement to incentivize long-term solar-aligned charging behavior. +* **L10 Token Engine v4.3.5/6 Parity:** Standardized payload extraction for Kafka-driven engagement triggers, maintaining seamless reward auditing across the 10-layer stack. + +### Backlog Updates +* **[L6-127] IMPLEMENTED:** Robust Site ID extraction in `handleGridSignal` (Supporting `site_id`, `siteId`, `location_id`, `locationId`). +* **[L6-128] IMPLEMENTED:** "Solar Flare" Achievement (25 cumulative Solar Ramp responses). +* **[L6-129] COMPLETED:** Version increment to v5.15.0 for platform-wide parity. + +### Engineering Execution +* **Grid Signal Logic:** Refactored `handleGridSignal` to prioritize multiple site keys from incoming Kafka payloads, ensuring compatibility with evolving L7 and L2 telemetry conventions. +* **Solar Alignment:** Deployed `checkSolarFlareAchievement` to reward drivers who consistently respond to CAISO/ERCOT solar ramp signals, reinforcing grid stability during peak solar production. +* **Version Synchronization:** Updated all internal health checks and package manifests to reflect v5.15.0 status. + +--- +*“Behavior Drives the Grid. Precision Drives the Behavior.”* diff --git a/services/06-engagement-engine/index.js b/services/06-engagement-engine/index.js index 48037515..6e668109 100644 --- a/services/06-engagement-engine/index.js +++ b/services/06-engagement-engine/index.js @@ -123,7 +123,7 @@ initKafka().catch(console.error); app.get('/health', (req, res) => { res.json({ service: 'engagement-engine', - version: '5.14.0', // Weekly Mission: Phase 6 AI Readiness & Multi-Site Achievements + version: '5.15.0', // Weekly Mission: Robust Site ID Extraction & Solar Flare Achievement status: 'healthy', layer: 'L6' }); @@ -729,6 +729,7 @@ async function handleAdvanceChargeSignal(payload) { // Check for Solar Surge achievement await checkSolarSurgeAchievement(driverId); + await checkSolarFlareAchievement(driverId); } } catch (error) { console.error('[Engagement] Error handling Advance Charge signal:', error); @@ -748,8 +749,9 @@ async function handleVPPParticipationUpdate(payload) { } async function handleGridSignal(payload) { - const { event_id, priority, site_id } = payload; - console.log(`[L6] Handling Grid Signal for Team Challenge: ${event_id} (${priority}) - Site: ${site_id}`); + const { event_id, priority } = payload; + const siteIdVal = payload.site_id || payload.siteId || payload.location_id || payload.locationId || 'ALL'; + console.log(`[L6] Handling Grid Signal for Team Challenge: ${event_id} (${priority}) - Site: ${siteIdVal}`); try { // 1. Fully Bulked Database Operation: @@ -868,7 +870,7 @@ async function handleGridSignal(payload) { LEFT JOIN completed_challenges cc ON td.driver_id = cc.driver_id LEFT JOIN new_achievements na ON td.driver_id = na.driver_id GROUP BY td.driver_id, td.iso - `, [site_id || 'ALL', JSON.stringify({ event_id })]); + `, [siteIdVal, JSON.stringify({ event_id })]); // 2. Optimized Notification & L10 Payout Handling for (const row of results.rows) { @@ -1490,6 +1492,24 @@ async function checkSolarSurgeAchievement(driver_id) { } } +async function checkSolarFlareAchievement(driver_id) { + try { + const count = await pool.query(` + SELECT COUNT(*) FROM driver_actions + WHERE driver_id = $1 AND action_type = 'solar_ramp_response' + `, [driver_id]); + + if (parseInt(count.rows[0].count) >= 25) { + const achievement = await pool.query("SELECT id FROM achievements WHERE name = 'Solar Flare'"); + if (achievement.rows.length > 0) { + await awardAchievement(driver_id, achievement.rows[0].id); + } + } + } catch (error) { + console.error('[Engagement] Error checking Solar Flare achievement:', error); + } +} + async function checkSustainabilityChampion(driver_id) { // 100% Compliance Check: Verify at least one valid session for each of the last 30 consecutive days. // This enforces the "Green Audit" (<15% variance) via the L1-verified 'is_valid' flag. @@ -1730,6 +1750,7 @@ module.exports = { pool, redisClient, processChargingEvent, + handleGridSignal, checkHighConfidenceAchievement, checkSiteHarmonyAchievement, updateChallengeProgress, diff --git a/services/06-engagement-engine/package.json b/services/06-engagement-engine/package.json index a3e61af1..f07f77e4 100644 --- a/services/06-engagement-engine/package.json +++ b/services/06-engagement-engine/package.json @@ -1,6 +1,6 @@ { "name": "@migrid/engagement-engine", - "version": "5.13.0", + "version": "5.15.0", "description": "Gamification, leaderboards, and driver engagement", "main": "index.js", "scripts": { diff --git a/services/06-engagement-engine/v5_15_0_site_id.test.js b/services/06-engagement-engine/v5_15_0_site_id.test.js new file mode 100644 index 00000000..2f46b949 --- /dev/null +++ b/services/06-engagement-engine/v5_15_0_site_id.test.js @@ -0,0 +1,65 @@ +/** + * Verification script for L6 v5.15.0: Robust Site ID Extraction + */ +const { handleGridSignal, pool } = require('./index'); + +// Mock Kafka producer +const mockProducer = { + send: jest.fn().mockResolvedValue(true) +}; + +// Mock the global producer used in index.js +require('./index').producer = mockProducer; + +describe('L6 v5.15.0 Robust Site ID Extraction', () => { + beforeEach(() => { + jest.clearAllMocks(); + // Mock pool.query to return empty results by default to avoid DB errors during logic check + pool.query = jest.fn().mockResolvedValue({ rows: [] }); + }); + + test('handleGridSignal extracts site_id correctly', async () => { + const payload = { event_id: 'evt-1', priority: 'HIGH', site_id: 'SITE-A' }; + await handleGridSignal(payload); + expect(pool.query).toHaveBeenCalledWith( + expect.stringContaining('($1 = \'ALL\' OR chr.location_id = $1)'), + ['SITE-A', expect.any(String)] + ); + }); + + test('handleGridSignal extracts siteId correctly', async () => { + const payload = { event_id: 'evt-2', priority: 'HIGH', siteId: 'SITE-B' }; + await handleGridSignal(payload); + expect(pool.query).toHaveBeenCalledWith( + expect.stringContaining('($1 = \'ALL\' OR chr.location_id = $1)'), + ['SITE-B', expect.any(String)] + ); + }); + + test('handleGridSignal extracts location_id correctly', async () => { + const payload = { event_id: 'evt-3', priority: 'HIGH', location_id: 'SITE-C' }; + await handleGridSignal(payload); + expect(pool.query).toHaveBeenCalledWith( + expect.stringContaining('($1 = \'ALL\' OR chr.location_id = $1)'), + ['SITE-C', expect.any(String)] + ); + }); + + test('handleGridSignal extracts locationId correctly', async () => { + const payload = { event_id: 'evt-4', priority: 'HIGH', locationId: 'SITE-D' }; + await handleGridSignal(payload); + expect(pool.query).toHaveBeenCalledWith( + expect.stringContaining('($1 = \'ALL\' OR chr.location_id = $1)'), + ['SITE-D', expect.any(String)] + ); + }); + + test('handleGridSignal defaults to ALL if no site ID provided', async () => { + const payload = { event_id: 'evt-5', priority: 'HIGH' }; + await handleGridSignal(payload); + expect(pool.query).toHaveBeenCalledWith( + expect.stringContaining('($1 = \'ALL\' OR chr.location_id = $1)'), + ['ALL', expect.any(String)] + ); + }); +}); diff --git a/services/06-engagement-engine/v5_7_0_logic.test.js b/services/06-engagement-engine/v5_7_0_logic.test.js index b068ea3a..ee7caaa0 100644 --- a/services/06-engagement-engine/v5_7_0_logic.test.js +++ b/services/06-engagement-engine/v5_7_0_logic.test.js @@ -56,9 +56,9 @@ describe('L6 Engagement Engine v5.8.0 Logic and Alignment', () => { // server.close(); // Not needed if not listening }); - test('Health check returns correct version v5.14.0', async () => { + test('Health check returns correct version v5.15.0', async () => { const res = await request(app).get('/health'); - expect(res.body.version).toBe('5.14.0'); + expect(res.body.version).toBe('5.15.0'); }); test('Database contains expected achievement functions', async () => {