From 33bfec838ae175e45cd772481c72a46ac4ba4ab2 Mon Sep 17 00:00:00 2001 From: Paul Trowbridge Date: Thu, 17 Sep 2026 14:18:24 -0400 Subject: [PATCH] Say why the ordering columns are missing instead of failing silently syncOrderExpressions caught its errors and logged them, and returned quietly when there was nothing to build. Those two outcomes are indistinguishable from the outside -- the Bucket and Segment columns are simply absent -- which is most of why this has taken several rounds to pin down. A restore() rejection now raises a message in the status bar, and the no-ordering case logs the inputs it saw: the version's bucket_order and every log entry carrying a seq. One reload should say which of the two is happening. Co-Authored-By: Claude Opus 5 (1M context) --- ui/src/views/Forecast.jsx | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/ui/src/views/Forecast.jsx b/ui/src/views/Forecast.jsx index 2da34f5..4fbd522 100644 --- a/ui/src/views/Forecast.jsx +++ b/ui/src/views/Forecast.jsx @@ -919,6 +919,10 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio // // Removing an ordering removes its column: leaving a stale Bucket expression // behind would keep ordering by an order that no longer exists. + function dbgOrder(msg, extra) { + console.log(`[pf-order] ${msg}`, extra) + } + async function syncOrderExpressions() { const viewer = viewerRef.current if (!viewer) return @@ -934,7 +938,19 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio Object.assign(next, wanted) const same = JSON.stringify(next) === JSON.stringify(current) - if (same) return + if (same) { + // Nothing to do is a legitimate outcome, but "nothing was ordered" and + // "the ordering is already applied" look identical from the outside. + if (Object.keys(wanted).length === 0) { + dbgOrder('no ordering to apply', { + bucket_order: version?.bucket_order, + seqs: Object.entries(logMeta || {}) + .filter(([, m]) => m.seq != null) + .map(([id, m]) => `${id}:${m.tag || m.note}=${m.seq}`), + }) + } + return + } // An expression the pivot is using cannot simply vanish; drop it from the // axes first or restore() rejects the config. @@ -948,8 +964,13 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio split_by: (cfg.split_by || []).filter(stillValid), sort: (cfg.sort || []).filter(([c]) => stillValid(c)), }) + dbgOrder('applied', Object.keys(next)) } catch (err) { - console.error('[syncOrderExpressions]', err) + // Surfaced rather than logged: this failing silently is what made the + // missing Bucket and Segment columns so hard to pin down -- the columns + // simply were not there, with nothing to say why. + console.error('[syncOrderExpressions]', err, { wanted, next }) + flash(`Column ordering failed: ${err.message || err}`, 'error') } }