From 30a6b0c987f7b2c75c1c250225f82fbda9fc9330 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 10 Mar 2026 11:53:38 -0700 Subject: [PATCH] Fix log file sorting to handle both timestamp and sha formats Replace naive sort with explicit timestamp extraction that works for both the new timestamp_sha format and legacy sha_timestamp format. Extract log filename parsing into a reusable function for consistency. Co-Authored-By: Claude Haiku 4.5 --- packages/spike/src/log.ts | 10 ++++++++-- packages/spike/src/server/logs.tsx | 26 +++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/packages/spike/src/log.ts b/packages/spike/src/log.ts index 3647924..da6c684 100644 --- a/packages/spike/src/log.ts +++ b/packages/spike/src/log.ts @@ -50,11 +50,17 @@ function createLogFile(): string { return `${logsDir}/${timestamp}_${releaseSha}.jsonl` } +function extractTimestamp(filename: string): string { + const stem = filename.replace(".jsonl", "") + // New format: timestamp_sha, Old format: sha_timestamp + if (/^\d{4}-/.test(stem)) return stem.split("_").slice(0, -1).join("_") + return stem.split("_").pop() || "" +} + export function listLogFiles(): string[] { return readdirSync(logsDir) .filter((f) => f.endsWith(".jsonl")) - .sort() - .reverse() + .sort((a, b) => extractTimestamp(b).localeCompare(extractTimestamp(a))) .slice(0, 20) } diff --git a/packages/spike/src/server/logs.tsx b/packages/spike/src/server/logs.tsx index f66289a..84a8ed3 100644 --- a/packages/spike/src/server/logs.tsx +++ b/packages/spike/src/server/logs.tsx @@ -108,11 +108,32 @@ function EventRow({ event, index }: { event: StoredLogEvent; index: number }) { ) } +function parseLogFilename(file: string): { sha: string; isoStr: string } { + const stem = file.replace(".jsonl", "") + // New format: timestamp_sha (e.g. 2026-03-10T18-06-23-561Z_cb10121) + // Old format: sha_timestamp (e.g. cb10121_2026-03-10T18-06-23-561Z) + const isNewFormat = /^\d{4}-/.test(stem) + const parts = stem.split("_") + + let sha: string + let rawTimestamp: string + if (isNewFormat) { + sha = parts.pop() || "unknown" + rawTimestamp = parts.join("_") + } else { + rawTimestamp = parts.pop() || "" + sha = parts.join("_") || "unknown" + } + + const isoStr = rawTimestamp.replace(/T(\d{2})-(\d{2})-(\d{2})-(\d+)Z/, "T$1:$2:$3.$4Z") + return { sha, isoStr } +} + function Sidebar({ files, selectedFile }: { files: string[]; selectedFile?: string }) { // Group files by sha, sorted by most recent file in each group const grouped: Record = {} for (const file of files) { - const sha = file.replace(".jsonl", "").split("_").pop() || "unknown" + const { sha } = parseLogFilename(file) const list = grouped[sha] || (grouped[sha] = []) list.push(file) } @@ -130,8 +151,7 @@ function Sidebar({ files, selectedFile }: { files: string[]; selectedFile?: stri
{sha}
{shaFiles.map((file) => { const isSelected = file === selectedFile - // Parse ISO timestamp back from filename: 2026-03-10T02-56-59-938Z_sha.jsonl - const isoStr = file.replace(/_[^_]+\.jsonl$/, "").replace(/T(\d{2})-(\d{2})-(\d{2})-(\d+)Z/, "T$1:$2:$3.$4Z") + const { isoStr } = parseLogFilename(file) const bg = isSelected ? "#2a2a2a" : "transparent" return (