()
| 245 | } |
| 246 | |
| 247 | function main(): void { |
| 248 | const opts = parseArgs(process.argv); |
| 249 | if (!opts.sessionPrefix) { |
| 250 | console.error( |
| 251 | "usage: bun scripts/analyze-cache-busts.ts <sessionIdPrefix> [--dir <path>] [--since ISO] [--until ISO] [--limit N] [--show-diff] [--all-busts]", |
| 252 | ); |
| 253 | process.exit(1); |
| 254 | } |
| 255 | const snaps = loadSnapshots(opts); |
| 256 | if (snaps.length === 0) { |
| 257 | console.error(`No dumps found for session prefix "${opts.sessionPrefix}" in ${opts.dir}`); |
| 258 | process.exit(1); |
| 259 | } |
| 260 | console.log(`Session: ${snaps[0].session}`); |
| 261 | console.log(`Dumps: ${snaps.length} (dir: ${opts.dir})`); |
| 262 | console.log(""); |
| 263 | console.log("Dashboard times are local (UTC+2); table times are UTC."); |
| 264 | console.log( |
| 265 | "time(UTC) | segs | verdict | first-divergence | prevBytes → curBytes | cachedPrefix@breakpoint", |
| 266 | ); |
| 267 | console.log( |
| 268 | "-------------------|------|---------|-------------------------|-----------------------------|------------------------", |
| 269 | ); |
| 270 | |
| 271 | // By default only BUST rows are printed so a genuine prefix bust is never |
| 272 | // buried under ordinary tail-growth (STABLE) or no-op (SAME) rows. STABLE = |
| 273 | // divergence is past prev's last breakpoint (pure tail addition, still a |
| 274 | // cache hit). --all-rows restores the full per-request table. |
| 275 | let bustCount = 0; |
| 276 | for (let k = 0; k < snaps.length; k += 1) { |
| 277 | const cur = snaps[k]; |
| 278 | if (k === 0) { |
| 279 | if (opts.allRows) { |
| 280 | console.log( |
| 281 | `${fmtTime(cur.createdAt)} | ${String(cur.segments.length).padStart(4)} | BASE | (first request) | |`, |
| 282 | ); |
| 283 | } |
| 284 | continue; |
| 285 | } |
| 286 | const prev = snaps[k - 1]; |
| 287 | const idx = firstDivergence(prev.segments, cur.segments); |
| 288 | if (idx === -1) { |
| 289 | if (opts.allRows) { |
| 290 | console.log( |
| 291 | `${fmtTime(cur.createdAt)} | ${String(cur.segments.length).padStart(4)} | SAME | (identical to prev) | |`, |
| 292 | ); |
| 293 | } |
| 294 | continue; |
| 295 | } |
| 296 | const seg = cur.segments[idx] ?? prev.segments[idx]; |
| 297 | // The reusable cache was written at PREV's breakpoints. OpenCode moves the |
| 298 | // tail breakpoint forward every request, so judging against CUR's final |
| 299 | // breakpoint mislabels ordinary tail growth as a bust. |
| 300 | const prevLastBreakpoint = lastBreakpointIndex(prev.segments); |
| 301 | const verdict = idx > prevLastBreakpoint ? "STABLE" : "BUST"; |
| 302 | if (verdict === "BUST") bustCount += 1; |
| 303 | if (verdict !== "BUST" && !opts.allRows) continue; |
| 304 | const prevPrefix = cachedPrefixBytes(prev.segments, prev.segments.length); |
no test coverage detected