From 42bb7bb5cd38978018a1356e9a87d272dbc74bfd Mon Sep 17 00:00:00 2001 From: Ami Suzuki Date: Mon, 1 Dec 2025 17:47:58 -0600 Subject: [PATCH 1/2] fix: no labels before last turbine of catching up phase --- .../ShredsProgression/ShredsSlotLabels.tsx | 2 +- .../Overview/ShredsProgression/atoms.ts | 25 ++++++++++++++++--- .../shredsProgressionPlugin.ts | 20 +++++++-------- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/features/Overview/ShredsProgression/ShredsSlotLabels.tsx b/src/features/Overview/ShredsProgression/ShredsSlotLabels.tsx index bf1d7823..951790c1 100644 --- a/src/features/Overview/ShredsProgression/ShredsSlotLabels.tsx +++ b/src/features/Overview/ShredsProgression/ShredsSlotLabels.tsx @@ -24,9 +24,9 @@ export default function ShredsSlotLabels() { return ( diff --git a/src/features/Overview/ShredsProgression/atoms.ts b/src/features/Overview/ShredsProgression/atoms.ts index 09b7fe92..162bfa5a 100644 --- a/src/features/Overview/ShredsProgression/atoms.ts +++ b/src/features/Overview/ShredsProgression/atoms.ts @@ -4,6 +4,7 @@ import { maxShredEvent, ShredEvent } from "../../../api/entities"; import { delayMs, xRangeMs } from "./const"; import { nsPerMs, slotsPerLeader } from "../../../consts"; import { getSlotGroupLeader } from "../../../utils"; +import { startupFinalTurbineHeadAtom } from "../../StartupProgress/atoms"; type ShredEventTsDeltaMs = number | undefined; /** @@ -40,17 +41,35 @@ export function createLiveShredsAtoms() { min: number; max: number; }>(); + const rangeAfterStartupAtom = atom((get) => { + const range = get(_slotRangeAtom); + const startupFinalTurbineHead = get(startupFinalTurbineHeadAtom); + if (!range || startupFinalTurbineHead == null) return; + + // no slots after final turbine head + if (startupFinalTurbineHead + 1 > range.max) return; + + return { + min: Math.max(startupFinalTurbineHead + 1, range.min), + max: range.max, + }; + }); return { /** * min completed slot we've seen since we started collecting data */ minCompletedSlot: atom((get) => get(_minCompletedSlotAtom)), range: atom((get) => get(_slotRangeAtom)), + rangeAfterStartup: rangeAfterStartupAtom, + // leader slots after turbine head at the end of startup groupLeaderSlots: atom((get) => { - const range = get(_slotRangeAtom); - if (!range) return []; + const range = get(rangeAfterStartupAtom); + const startupFinalTurbineHead = get(startupFinalTurbineHeadAtom); + if (!range || startupFinalTurbineHead == null) return []; - const slots = [getSlotGroupLeader(range.min)]; + const min = Math.max(startupFinalTurbineHead + 1, range.min); + + const slots = [getSlotGroupLeader(min)]; while (slots[slots.length - 1] + slotsPerLeader - 1 < range.max) { slots.push( getSlotGroupLeader(slots[slots.length - 1] + slotsPerLeader), diff --git a/src/features/Overview/ShredsProgression/shredsProgressionPlugin.ts b/src/features/Overview/ShredsProgression/shredsProgressionPlugin.ts index 63d56a5b..b2410645 100644 --- a/src/features/Overview/ShredsProgression/shredsProgressionPlugin.ts +++ b/src/features/Overview/ShredsProgression/shredsProgressionPlugin.ts @@ -53,6 +53,7 @@ export function shredsProgressionPlugin( const slotRange = store.get(atoms.range); const minCompletedSlot = store.get(atoms.minCompletedSlot); const skippedSlotsCluster = store.get(skippedClusterSlotsAtom); + const rangeAfterStartup = store.get(atoms.rangeAfterStartup); const maxX = u.scales[shredsXScaleKey].max; @@ -67,6 +68,8 @@ export function shredsProgressionPlugin( // depending on connection time. Ignore those slots, and only draw slots // from min completed. if (minCompletedSlot == null) return; + + if (!rangeAfterStartup) return; } // Offset to convert shred event delta to chart x value @@ -184,9 +187,9 @@ export function shredsProgressionPlugin( u.ctx.restore(); - if (!isOnStartupScreen) { + if (!isOnStartupScreen && rangeAfterStartup) { updateLabels( - slotRange, + rangeAfterStartup, liveShreds.slots, skippedSlotsCluster, u, @@ -581,22 +584,17 @@ function getIncompleteBlockStart( const firstSlotNumber = blockSlotNumbers[0]; const startFirstSlotNumber = slots.get(firstSlotNumber)?.minEventTsDelta; - const prevBlockEnd = - previousBlock.type === "complete" - ? previousBlock.endTsDelta - : previousBlock.endTsDelta; + if (startFirstSlotNumber != null) return startFirstSlotNumber; - // incomplete block started at either start of first - // slot, or end of previous block - const blockStart = startFirstSlotNumber ?? prevBlockEnd; - if (blockStart == null) { + const prevBlockEnd = previousBlock.endTsDelta; + if (prevBlockEnd == null) { console.error( `Missing block start ts for incomplete block beginning at ${firstSlotNumber}`, ); return; } - return blockStart; + return prevBlockEnd; } type TsDeltaRange = [startTsDelta: number, endTsDelta: number | undefined]; From 7fba57fcd5f722d211bb83108d73d5ef64fe427c Mon Sep 17 00:00:00 2001 From: Ami Suzuki Date: Mon, 1 Dec 2025 20:16:23 -0600 Subject: [PATCH 2/2] fix: nullable schemas --- src/api/entities.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/entities.ts b/src/api/entities.ts index ebf6dd15..3f23ec08 100644 --- a/src/api/entities.ts +++ b/src/api/entities.ts @@ -122,8 +122,8 @@ export const catchUpHistorySchema = z.object({ }); export const estimatedSlotSchema = z.number(); -export const resetSlotSchema = z.number(); -export const storageSlotSchema = z.number(); +export const resetSlotSchema = z.number().nullable(); +export const storageSlotSchema = z.number().nullable(); export const voteSlotSchema = z.number(); export const slotCaughtUpSchema = z.number().nullable();