Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions MASTER_BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |

Expand Down Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions PLATFORM_STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand All @@ -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%.
Expand Down
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions services/06-engagement-engine/WEEKLY_REPORT_APRIL_2026_V5_15_0.md
Original file line number Diff line number Diff line change
@@ -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.”*
29 changes: 25 additions & 4 deletions services/06-engagement-engine/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
});
Expand Down Expand Up @@ -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);
Expand All @@ -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:
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -1730,6 +1750,7 @@ module.exports = {
pool,
redisClient,
processChargingEvent,
handleGridSignal,
checkHighConfidenceAchievement,
checkSiteHarmonyAchievement,
updateChallengeProgress,
Expand Down
2 changes: 1 addition & 1 deletion services/06-engagement-engine/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
65 changes: 65 additions & 0 deletions services/06-engagement-engine/v5_15_0_site_id.test.js
Original file line number Diff line number Diff line change
@@ -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)]
);
});
});
4 changes: 2 additions & 2 deletions services/06-engagement-engine/v5_7_0_logic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down