Skip to content

[pull] master from cube-js:master - #708

Merged
pull[bot] merged 7 commits into
code:masterfrom
cube-js:master
Sep 2, 2026
Merged

[pull] master from cube-js:master#708
pull[bot] merged 7 commits into
code:masterfrom
cube-js:master

Conversation

@pull

@pull pull Bot commented Sep 2, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? πŸ’– Please sponsor : )

MikeNitsenko and others added 7 commits September 2, 2026 14:11
* feat(cube-cli): list dbt sync history and read one sync's logs

`cube dbt` could start and follow a sync, but not look back at one. Two
commands complete it:

- `cube dbt history <deployment>` lists recent syncs β€” id, status, trigger,
  start, duration, branch β€” paged with `--first`/`--after`.
- `cube dbt logs <deployment> <sync-job-id>` prints a sync's phase timeline
  and the text a failed phase produced, colouring failure entries.

A duration is the server's own `durationMs` or an empty cell β€” never the
difference of two stamps written by different processes, which can disagree
with it and, for a run that fails moments after starting, be negative.

The two `--wait` failure messages now name `cube dbt logs` for the run that
failed, which is the difference between a CI step that explains itself and one
that only says "dbt sync failed"; both paths build that message through one
function instead of two copies.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(cube-cli): address dbt history and logs review feedback

- Every history cell is read the same way, through one `cell` reader: `status`
  keeps its single key, now with the reason it is the one field that cannot have
  a second spelling.
- Cells are bounded and single-line, so a value carrying a newline can no longer
  break the row it sits in, nor an unbounded one the layout.
- Server log text is stripped of control characters other than the line breaks
  and tabs the timeline keeps on purpose. `one_line` was never the guard it looks
  like: ESC is not whitespace, so a hostile dbt error could have retitled a
  window or overwritten the lines above it in a CI log.
- The raw-entry fallback no longer repeats the timestamp and stage the JSON
  already carries, and keeps its red when the entry says it is a failure β€” the
  entry this build understood least is the last place to drop that signal.
- `history`'s "could not read this" warning keys on the sync job id rather than
  on every cell being blank, which one filled column was enough to defeat.
- `human_duration_ms` rejects a float too large to cast, which saturated into a
  confident five-billion-hour duration instead of passing through.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat(cube-cli): filter dbt history, and time each phase in dbt logs

Aligns both commands with what the endpoints they call actually publish.

- `dbt history` takes `--status` and `--trigger`, sent through unchecked: the two
  vocabularies are the server's, and a filter this build has not heard of is one
  the server can still honour, where a list hard-coded here would refuse it.
- `dbt logs` drops `--first`/`--after`. One sync's timeline is one page, bounded
  by the number of phases it ran, so the flags were accepted here and ignored
  there β€” a promise of paging that does not exist.
- A log line now carries how long its phase took, sharing one bracket with the
  phase name so a multi-line failure is interrupted by neither. The timings are
  half of what makes this a timeline rather than a list of remarks.
- Fields are read under the names the endpoints publish, and only those: the
  second spellings were insurance taken out before the shapes were settled, and
  every one of them was dead. `status` is no longer the odd column out, since no
  column carries an alias now.

A listed run can be CANCELLED or UNKNOWN as well as the two the status endpoint
calls terminal. Nothing here acts on a status, so they pass through as they
arrived; the docs note that a cancelled run is still a failure to a `--wait`
gate, which needs a terminal answer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(cube-cli): refuse an empty dbt history filter, and spell its case out

- `--status` and `--trigger` carry a `nonempty_filter` parser, like every other
  free-text argument in the tree. An empty value is not dropped β€” `push` sends
  `status=` β€” so a CI script whose `$STATUS` did not expand would have listed
  whatever the server made of an empty filter.
- Both vocabularies are the server's and they do not share a case (statuses
  upper, triggers lower), so the help and the docs now spell that out: a
  mis-cased value is the one mistake that may come back as an empty table rather
  than as a complaint, and an empty table reads as an answer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(cube-cli): trim a dbt history filter, unlike a branch name

A filter is one word out of a vocabulary the server publishes, and no member of
it has a space in it β€” so ` FAILED` could only ever match nothing, landing in the
exact failure this argument's help was written to prevent: an empty table that
reads as an answer. `$(jq -r …)` and a value read out of a file are the ordinary
ways to acquire the padding.

The two helpers beside it still return what they were given, because a branch
name is the caller's own and `--branch '  x  '` can name a branch that exists.
A test pins the divergence rather than leaving it to be read as an oversight.

Also rewraps the docs paragraph the previous commit left one line too long.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(cube-cli): drop a field the run record does not carry

The fixture claimed an `updatedAt` on a listed run. The endpoint deliberately
does not publish one β€” the column behind it is frozen at the launch insert, so a
field with that name would never update β€” and a fixture that carries what the
transport does not is the kind of self-consistent wrong stub that green-lights a
reader nobody has actually exercised.

Nothing read it, so this is fidelity only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nularity switcher (#11718)

* docs(dashboards): document the user-attribute default on the time granularity switcher

The controls page documents this setting for filters, parent controls and
field switchers; the Time granularity switcher section stopped at the static
default, which is now only half the story.

Covers what the attribute is matched against (the granularity NAMES, not the
localized labels the control renders), what an unusable value does, and where
the attribute sits in the precedence order against a URL parameter, a viewer's
own pick and a parent control's mapping.

* docs(dashboards): scope the granularity attribute-default claims to what the control actually does

Review round, all three fair:

- The parent-control clause read unconditionally, but an option can leave a
  child empty β€” `resolveParentApplications` returns no application for it, so
  the attribute still seeds the switcher. Scoped, with the third state
  ("Reset to default", which goes to the SAVED default rather than the
  viewer's attribute) named as well.
- The match target is the switcher's ALLOWED granularities, not the full
  built-in set; naming that here saves a round trip to the paragraph below.
- Nested under "Default granularity" as `####`, so the section's opening
  "the default above" points at its enclosing heading β€” the shape the filter's
  and parent's versions already have.

* docs(dashboards): say exactly what Reset to default does to an attribute-seeded switcher

Verified against the implementation rather than asserted: `resolveParentApplications`
resolves a TIME_GRAIN reset to the child's saved `defaultGrain` and never consults
`userAttributeName`, and skips the child entirely when no default is saved β€” so the
attribute value survives that case. Both branches are now pinned by tests in
`parent-widget.spec.ts` (cubedevinc/cubejs-enterprise#14627).
…ashboard's filters (#11719)

* docs(dashboards): say that a chart opened in the workbook keeps the dashboard's filters

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(dashboards): describe the carried filters as they render, in the report's own filter bar

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(dashboards): name the From dashboard cue, and split the two questions the edit bullet answered

Review notes: colour was left as the only way to tell a carried filter from a
report one, and the chips do carry a "From dashboard" tooltip β€” so name it and
keep the tint as the at-a-glance cue. The second bullet asked "can I change
this?" and answered "edit the dashboard control", which is about what viewers
get, not about the person in the workbook wanting another value; those are now
separate, and the multi-sentence bullets end in full stops.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(dashboards): name the field switcher in the controls list, and keep the third bullet under its lead-in

Review nits: the page's controls list predated field switchers and this PR's
new paragraph names them, so the omission had become an in-page contradiction;
and the third bullet opened on the UI affordance rather than continuing "they
are not the report's".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* docs: sync API reference for platform-client v0.6.0

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WS9D79N13zEksXB13WeSRa

* fix(docs): hoist nullable-field metadata and de-collide SCIM sidebar groups

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WS9D79N13zEksXB13WeSRa

* fix(docs): scope the nullable-metadata hoist to the exact two-branch shape

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WS9D79N13zEksXB13WeSRa

* style(docs): trim the hoist function's comment

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WS9D79N13zEksXB13WeSRa

---------

Co-authored-by: Claude <noreply@anthropic.com>
* docs(dashboards): a parent control can drive a field switcher

CUB-4269 removes the limitation the Controls page documented in two places:
a field switcher can now be a parent control's child, so it appears in the
Children picker, its mapping row renders its own member picker, and it carries
the same mapping-status indicator the other child controls do.

Also documents `ms_`, the field switcher's own URL parameter. It shipped with
the control (CUB-454/CUB-4159) but the page still said a viewer's choice was
not carried in the link. It travels in a shared link and β€” unlike `f_` / `tg_`
β€” not in a scheduled export or a screenshot, whose URLs are rebuilt from a
server-side allowlist that has no entry for it.

* docs(dashboards): resolve the ms_ contradiction the first pass left behind

Review caught that "Sharing the current selection" still carried a bullet
saying a field switcher "has no parameter of its own" β€” nine lines below the
`ms_` row this branch adds. The page contradicted itself on its most
load-bearing new claim.

That bullet now states what actually happens, keeping the boundary the new Note
draws: the pick travels in a shared link, and does not survive a scheduled
export or a screenshot, whose URLs are rebuilt server-side from an allowlist
with no field-switcher parameter. The bullet above it widens from "filters and
granularities" to every control type, since a parent's children can now include
a switcher.

Also widens the table's explanatory paragraph from "semantic view and dimension"
to "member" and says the right-hand side of `ms_` is an internal name too β€” and
a measure when the switcher's Field Type is Measure.

And adds the missing `ms_` row to the embedded-dashboard URL table, which the
controls page sends readers to for "the same format" and which still listed
only `f_` and `tg_`.

* docs(dashboards): close the remaining two-of-three control enumerations

Review found the same pattern in three more places, all of which now under-count
the control types:

- the "Sharing the current selection" intro still promised the recipient "the
  same filters and granularities", eighteen lines above the `ms_` row;
- the "parameter is ignored" enumeration listed two reject cases where there are
  now three β€” a selected member outside the switcher's Alternatives is dropped
  the same way an unlisted granularity is (`dashboard-member-switchers.ts`
  drops a URL member that `isSelectable` rejects), and the page already commits
  to exactly that behaviour for the attribute path;
- the parent user-attribute example named the two child types it used to have.

On the embed page, the prose quoted `<semantic_view>` / `<dimension>` as
literals, so neither of the `ms_` row's own placeholders was covered by the rule
governing them β€” it now describes the parts instead β€” and the setting is named
**Field Type**, as the control spells it, on the page a reader reaches without
that context. Its reject enumeration gains the member case too.

Deliberately not touched: the embed page's section heading (and the link text
mirroring it) still say "filters and granularities". Renaming a published
heading is a wider change than this PR, and its anchor is explicit, so the
wording is a follow-up rather than a correctness gap.

* docs(dashboards): say member, not dimension, where the clause now governs ms_

Follow-through on the reject enumeration this branch just widened. The first
case in that same sentence still said "no matching control for that dimension",
but the clause governs all three parameters now and an `ms_` key is a measure
whenever the switcher's Field Type is Measure β€” the case the paragraph two above
goes out of its way to spell out. Both pages now say "member", matching the
vocabulary shift already made at the table.

On the embed page, **Alternatives** is also bolded and linked to the field
switcher section, so it reads like its sibling in the same sentence (the
allowed-granularities link) rather than as plain prose β€” that page is the one a
reader reaches without the controls page's context.

* docs(dashboards): document the field switcher's precedence, and stop saying it twice

Three follow-ups from review, all created by this branch rather than pre-existing:

- The field switcher's **User attribute default** section was the only one of the
  three that never said how the attribute interacts with a URL parameter or a
  parent control β€” and before this branch neither applied to it, so the gap is
  ours. It now states the whole chain, matching the time granularity twin.
  Verified rather than mirrored: `dashboard-member-switchers.ts` seeds
  `urlMember ?? attributeMember ?? defaultOption ?? replacedMember`, an unmapped
  parent cell emits no application at all (so the attribute's seed stands), and
  RESET targets `defaultOption ?? replaced member` without re-resolving the
  attribute.
- The field switcher `<Note>` had become a second copy of the Sharing bullet,
  which is the fuller version and sits in the section that owns the topic β€”
  `docs-mintlify/CLAUDE.md:267` ("Say it once"). The callout keeps the pointer
  and drops the mechanism.
- Re-wrapped the embed paragraph to the file's ~80 columns; taking the earlier
  suggestion verbatim had left it ragged with "For" stranded.

* docs(dashboards): name the resolution in the Note, and finish the re-flow

Trimming the Note left it saying an export renders "the control's default
member", where the bullet it defers to says "the default option β€” or the
recipient's own user attribute where one is set". Inconsistent about the same
mechanism, and read most often by an author who has just turned the attribute
default on two sections above. It now names the resolution instead of one of its
outcomes: those surfaces open on whatever member the control resolves for the
recipient on its own.

The previous re-wrap fixed the first three lines but left "Type** is
**Measure**. For" at 26 columns β€” the stranded break that was the point of the
report. Closing it needs the following sentence pulled up, past where the
suggestion block reached; the paragraph now flows at ~78 with only the URL line
long, as it already was.

* docs(dashboards): the replaced member is offered too, so ms_ back to it is honoured

Both reject clauses said a member "isn't among the field switcher's
Alternatives", which is narrower than the code: `isSelectable` is
`candidate === member || options.includes(candidate)`, so the replaced member is
selectable as well. `ms_orders.status=status` β€” the hand-written way to deep-link
back to the original view β€” is honoured, and both pages told the reader it was
ignored.

The embed page matters more here, since it is about hand-writing the parameter,
but the controls page's sentence sits directly under "you can also write these
parameters by hand", so both are read by someone acting on the rule.

Left as-is: the attribute paragraph's identical shorthand, which is pre-existing
and sits in a passage that is not about authoring a value.

* docs(dashboards): a parent is the third thing an export can resolve a switcher to

The Sharing bullet named the default option and the recipient's attribute, but a
parent control driving the switcher is the path this branch itself added β€” and
in a server-rendered copy there is no `ms_`, so the parent opens on its own
default (or the recipient's attribute) and drives its children.

Verified both halves in `dashboard-parent.ts`: a plain `defaultOptionId` needs no
mount-time write because `persistSelection` already wrote the mapped member into
the child's own `defaultOption`, and a user-attribute parent pushes on mount
(`applyParentOption(..., { isSeeding: true })`) because no saved child config can
carry a per-viewer value. Either way the switcher shows what the parent maps.

Same over-statement as the Note fixed one commit ago; here the enumeration is
worth keeping since this is the section that owns the topic, so it is extended
rather than replaced.

* docs(dashboards): name the setting in the Reset row, not the outcome

The field switcher's cell said Reset puts it back to "the member it opens on",
which for a viewer with a user-attribute default IS the attribute's member β€” the
exact resolution the clause added one commit ago says Reset does not perform.
`getSwitcherDefaultMember` returns `defaultOption ?? replaced member` and never
consults the attribute, so the row now names the setting, as its two siblings
already did.

The default-granularity link goes in alongside for symmetry: a reader who needs
"what is the default option" needs the same answer for granularity.

* docs(dashboards): the mapping picker offers the replaced member too

Last instance of the Alternatives narrowing, and here it made the sentence
contradict its own next clause: the picker was described as Alternatives-only
while the following sentence promised "exactly the ones a viewer could pick".

Checked the editor rather than the analogy, as the review asked:
`ParentChildValueControl` builds the row's options with `resolveSwitcherOptions`,
passing `memberName: parsed.memberName`, and that helper PREPENDS the replaced
member (`[memberName, ...configured]`). So the picker does offer it, and
"exactly" was the accurate half.

Also re-read the section as one claim rather than fixing this line alone β€”
L150 (offered set), L176 (precedence), L193, L225 (Reset), L314 and L317 now
tell the same story. L176's "outranks both" is left as-is deliberately: it
mirrors the time-granularity twin's established phrasing at L132, so changing
only the switcher's copy would introduce a divergence rather than remove one.

* docs(dashboards): drop the export carve-out β€” CUB-4289 landed

Verified on master rather than taking the report: #14633 merged at 11:10 today
and `ALLOWED_CAPTURE_PARAM_PREFIXES` is now `['f_', 'tg_', 'ms_']`, with
`buildFilterQueryParams` taking `memberSwitcherState` as a required argument and
a `DashboardMemberSwitcherInput` carrying the selection through scheduled runs.
So a field switcher's member now reaches every server-rendered copy, and both
sentences this branch added about it not surviving one are false.

The `<Note>` is deleted rather than inverted: it existed only to record
limitations, and with the last one gone there is no caveat to call out β€” the
positive statement already lives in the section that owns the topic, so
inverting it here would re-create the duplication removed in 74e5fd3.

The Sharing bullet goes for the same reason: its entire content was the
carve-out, and "Every control's pick, together" already covers all three types.

Deliberately NOT documenting the new export behaviour here β€” #11723
owns that and describes it more fully (link carries only what the viewer chose;
an export carries every switcher and outranks an attribute; a scheduled export
renders the member stored on the schedule).
…st two (#11738)

* docs(dashboards): an export carries every control's selection, not just two

The Dashboards page told readers that a PNG/PDF snapshot carries "the filter and
time-grain selections you currently have applied in your browser". That was
exhaustive when it was written and stopped being so when CUB-4289 landed
(cubedevinc/cubejs-enterprise#14633): a field switcher's member now reaches every
server-rendered copy too, so the sentence enumerated two of the three things that
travel and silently excluded the third.

Left behind by the two PRs that covered this on the Controls and embedding pages β€”
different file, so neither touched it, and a reader who only ever opens the
Dashboards overview would still have concluded their switcher does not survive an
export.

Scope is deliberately just this sentence. `memberSwitchers`, the API field for the
scheduled case, is not hand-documented here because `filters` and `timeGrains`
aren't either β€” those reach readers through the generated management-API
reference, and the new field lands there with the next SDK release.

* docs(dashboards): scope the export claim to a download you start yourself

Review caught that the sentence was over-broad in a way the page then invites the
reader to act on. Three paragraphs down it says the same screenshot mechanism
powers PNG/PDF attachments on scheduled notifications β€” so "the selections you
currently have applied in your browser are carried into the export" reads as
covering the scheduled case, where there is no browser session at all and the
attachment renders whatever the notification itself carries.

That over-broadness predates this PR: the original sentence made the same claim
about filters and time grains. Fixed here rather than left alone, since this PR is
what draws attention to the sentence.

Also from review: "time granularity switcher" is what controls.mdx calls the
second control, so the list now names all three the same way instead of mixing a
thing ("time grains") with two control types; and the paragraph is re-wrapped to
the file's ~76 columns.

* docs(dashboards): don't send readers to a notification setting that has no UI

"Renders the selections configured on the notification" promised something a
reader can go and find. They can't: the notification card is delivery channel β†’
recipients β†’ attachment format β†’ AI summary, and notifications.mdx documents only
PNG vs PDF about the attachment. The capability is real but API-only, and
`memberSwitchers` isn't in the checked-in spec yet β€” so linking the management API
would fail them too, three paragraphs above a link to the page that already would.

Also fixes the assumption underneath it: the sentence implied a notification always
carries selections. Most don't. With none configured `buildDashboardFilterParams`
returns `undefined`, the capture URL is left untouched, and the board renders its
own defaults β€” which is the common case and now the one stated first.
@pull pull Bot locked and limited conversation to collaborators Sep 2, 2026
@pull
pull Bot merged commit 66210e3 into code:master Sep 2, 2026
2 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants