The panel crashed at runtime with "(ds.topports ?? []).slice is not a function" because Plan 5 assumed shapes that didn't match what the Phase 2 DShield collector actually emits. Reality: topports: Record<string, Port> // dict keyed "0".."N", not Array port field: targetport // not "port" topips[].source // the IP — not "ip" topips[].reports // the count — not "records" topips // has no country/CC field dailysummary[].date // ISO date string, ascending order toArray() helper accepts both Record<string,T> and T[] defensively (so either shape works if the backend changes its mind). Both lists sort by rank before slicing. The CC column is dropped (data doesn't carry it); its slot becomes the per-source target count which the data does carry. Daily summary picks the latest entry by date instead of index 0 (the order is ascending, so [0] would be the oldest day). Bigger discovery: most snapshot keys (cve_new / kev_added / ransomware_victim / coinbase_price / etc.) are LATEST-event payloads, not the recent-list arrays Plan 5 anticipated. Tasks 15-21 will need per-topic Zustand stores that accumulate over time, populated by Task 24 from snapshot bootstrap + WS messages. Will discuss with Carter before committing to that architecture. |
||
|---|---|---|
| .. | ||
| advanced | ||
| beginner | ||
| intermediate | ||