Skip to content

feat(server-core): make dev mode opt-in and stop respecting NODE_ENV - #11959

Open
paveltiunov wants to merge 64 commits into
masterfrom
claude/dev-mode-default-behavior-79chik
Open

paveltiunov wants to merge 64 commits into
masterfrom
claude/dev-mode-default-behavior-79chik

Conversation

@paveltiunov

@paveltiunov paveltiunov commented Sep 22, 2026

Copy link
Copy Markdown
Member

Check List

  • Tests have been run in packages where changes have been made if available
  • Linter has been run for changed code
  • Tests for the changes have been added if not covered yet
  • Docs have been added / updated if required

Description of Changes Made

Development mode was on whenever NODE_ENV was anything but production, so an
instance with neither NODE_ENV nor CUBEJS_DEV_MODE set came up in dev mode —
an authentication bypass — without anyone asking for it.

Dev mode is now decided by CUBEJS_DEV_MODE alone and defaults to false. NODE_ENV
is deprecated for this decision and ignored; a one-time warning is printed when a
non-production NODE_ENV is seen with CUBEJS_DEV_MODE unset.

The decisions that keyed off NODE_ENV as a stand-in for dev mode now follow the
resolved dev mode:

Where Was Now
env.ts devMode CUBEJS_DEV_MODE only, but callers OR'd in NODE_ENV single source of truth, off by default, warns on deprecated NODE_ENV use
OptsHandler.isDevMode() NODE_ENV !== 'production' || devMode CreateOptions.devServer ?? devMode
CUBEJS_LOG_REDACTION default off unless NODE_ENV=production off only in dev mode
CubejsServerCore logger dev logger unless NODE_ENV=production dev logger only in dev mode
ApiGateway.enforceSecurityChecks NODE_ENV === 'production' !devServer
GraphiQL IDE exposed unless NODE_ENV=production exposed only in dev mode
SQLServer native logger prod logger on NODE_ENV=production prod logger outside dev mode
DatabricksDriver pre-agg schema dev_pre_aggregations unless NODE_ENV=production follows dev mode
CubeSQL ConfigObjImpl::default (Rust) its own copy of the isDevMode rule, feeding the SQL API's CUBEJS_LOG_REDACTION default CUBEJS_DEV_MODE only

CreateOptions.devServer is authoritative where an embedder set it, and the gateway,
CubejsServerCore and OptsHandler all resolve it the same way, so no two of them can
disagree about whether an instance is a dev server.

cubejs dev-server. It asks for dev mode through CreateOptions.devServer, not by
writing CUBEJS_DEV_MODE. That matters because the variable also gates the SQL API's
default port and its password check: writing it would have served an unauthenticated SQL
API wherever a port is configured, which master never did. With it left unset, everything
that follows the resolved dev mode sees a dev server, and everything still keyed on the
variable — pgSqlPort, the SQL password check, the Databricks schema — behaves exactly
as on master. An explicit CUBEJS_DEV_MODE wins over the command, and a devServer in
cube.js wins over both. NODE_ENV is synced from the resolved value as a compatibility
shim for user config code, and the CLI calls markDevModeResolvedByCaller() so the
deprecation warning does not fire against Cube's own NODE_ENV.

DevServer.initDevEnv only runs in dev mode, so its NODE_ENV-branched banner collapsed
to the single "authentication checks are disabled" line.

Behavior changes worth calling out

  • An embedded server-core with no env vars now throws
    Either CUBEJS_DB_TYPE or CreateOptions.driverFactory must be specified instead of
    silently starting a dev server. That is the point of the change, but it is a visible
    break for anyone who relied on the implicit dev mode.
  • An embedder passing devServer: true under NODE_ENV=production with
    CUBEJS_DEV_MODE unset loses JWT enforcement.
    On master that config mounted
    Playground while the data APIs stayed authenticated; dev mode now follows the option,
    so /v1/load and GraphQL accept requests with no token, GraphiQL and stack traces are
    served, log redaction defaults off and pre-aggregations move to dev_pre_aggregations.
    This is the only change here that turns authentication off where it was on, and it has
    its own paragraph in DEPRECATION.md.
  • The mirror config, devServer: false with CUBEJS_DEV_MODE=true, loses the bundled
    Cube Store default and moves from dev_pre_aggregations to prod_pre_aggregations.
    It also keeps the SQL API on CUBEJS_DEV_MODE, so the Postgres endpoint stays open on
    15432 while the REST and GraphQL APIs start enforcing JWT — called out in
    DEPRECATION.md with both remedies.
  • An instance that was implicitly in dev mode switches from the human-readable logger to
    one JSON object per line, which breaks anything that greps Cube's stdout.
  • CUBEJS_DEV_MODE=true combined with an explicit NODE_ENV=production no longer
    enforces JWT checks. Under the CLI and the official Docker images that combination
    already behaved this way, so only direct server-core embedders with that
    contradictory config are affected.

All of these are documented in DEPRECATION.md.

The pre-aggregation schema pin

A driver never receives CreateOptions, so DatabricksDriver.getPreAggrSchemaName()
the only reader of CUBEJS_PRE_AGGREGATIONS_SCHEMA outside server-core, and only when a
catalog is configured — would resolve its own schema from CUBEJS_DEV_MODE and could
name a different one than the instance. CubejsServerCore now writes the schema it
resolved into that variable when the user has not set one, as the last statement of its
constructor, and releases it on shutdown. Shares are keyed by identity, so a repeated
shutdown cannot spend another instance's share and a reload's drop invalidates the shares
it drops. A user-set value is never overwritten, and a per-tenant preAggregationsSchema
function has no single schema to write, so that case is left alone and warned about.

Deliberately left alone

NODE_ENV still drives two defaults that are not the dev-mode decision:
refreshWorkerMode (env.ts, background refresh on when NODE_ENV !== 'production')
and detectQueueAndCacheDriver (QueryOrchestrator.ts, cubestore vs memory). Switching
those would silently stop background refresh or change the queue driver for bare setups.
Both sites carry a comment saying so.

Two reads stay on CUBEJS_DEV_MODE alone and so can disagree with the resolved value for
a devServer embedder: the SQL API password check (sql-server.ts), which is what keeps
cubejs dev-server on the generate-a-password path, and
DatabricksDriver.getPreAggrSchemaName(), which cannot see CreateOptions at all — a
driver receives no preAggregationsSchema, so fixing it properly means changing the
driver construction contract repo-wide. Both sites are commented and the Databricks case
is in DEPRECATION.md with the CUBEJS_PRE_AGGREGATIONS_SCHEMA workaround. Happy to
open the driver-config PR separately.

Docs

DEPRECATION.md covers the NODE_ENV removal, the pre-aggregation schema move, the
bundled Cube Store loss, the log format change, the SQL API asymmetry and both
CreateOptions.devServer directions. The dev-mode warning snippets, the CUBEJS_DEV_MODE,
CUBEJS_LOG_REDACTION and CUBEJS_PRE_AGGREGATIONS_SCHEMA reference entries, the Core
deployment pages and the dev-mode page were updated to drop the NODE_ENV guidance.

Testing

CI is green on the head of this branch: lint, unit (24.x, 3.13), unit (26.x, 3.13),
unit-core, build-cubestore, Check fmt/clippy, Unit (Rewrite Engine), CodeQL, the
native build matrix, every integration job and every driver suite.

Locally: yarn tsc and yarn lint clean; cubejs-backend-shared 549/549,
cubejs-server 22/22, cubejs-server-core OptsHandler 48/48 and index 46/46;
cargo fmt --all -- --check and cargo clippy --locked --all-targets -- -D warnings
clean in both rust/cubesql and packages/cubejs-backend-native. (cubejs-api-gateway
is covered by the unit job rather than locally — its native-dependent suite cannot run
in my container.)

New coverage for getEnv('devMode'), the deprecation warning, the log-redaction default
and its caller-resolved override, CreateOptions.devServer in both directions,
enforceSecurityChecks resolution, the full cubejs dev-server / cubejs server matrix
including the SQL API port sources, the NODE_ENV take-back in all four directions, and
the pre-aggregation schema pin's lifecycle — a refused pin, a throw before and after the
options resolve, a repeated shutdown, a share invalidated by a reload's drop, and the
conflict warning's two remediation branches.

Earlier red runs on intermediate commits were environmental and are triaged in the
comments: live-warehouse contention (Snowflake, BigQuery, MSSQL, Firebolt, one Databricks
shard), a crates.io outage on an aarch64 runner, a testcontainer that could not reach the
Debian repos, and RefreshScheduler › Exponential backoff, whose assertion this PR does
not touch — a patch for that one is proposed in a comment, for a separate PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LTfc44Gdfu7dEACHzqQD3s

@paveltiunov
paveltiunov requested review from a team and keydunov as code owners September 22, 2026 17:50
@mintlify

mintlify Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
cubed3 🟢 Ready View Preview Sep 22, 2026, 11:09 PM

💡 Tip: Enable Automations to automatically generate PRs for you.

@github-actions github-actions Bot added docs Issues that require a documentation improvement javascript Pull requests that update Javascript code data source driver driver:databricks cubejs-jdbc-driver labels Sep 22, 2026
@claude

claude Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @paveltiunov's task in 4m 46s —— View job


Review complete — the security fix is right; 1 high, 3 medium, 4 low. 5 inline comments posted.

Full review
  • Read PR diff and changed files
  • Check dev-mode call sites repo-wide for missed NODE_ENV usage
  • Review tests and docs
  • Handle prior review threads
  • Post findings

The core change is correct and the call-site sweep is complete — grep over packages/*/src for NODE_ENV|nodeEnv leaves only env.ts:304 (refreshWorkerMode), QueryOrchestrator.ts:55 (detectQueueAndCacheDriver) and container.ts:343 (gracefulShutdown), all of which the PR description accounts for. Docker (latest.Dockerfile:42 NODE_ENV=production, CMD ["cubejs", "server"]) and both CLI commands are unaffected, so the blast radius really is embedders, as claimed.

High — a bare instance now boots as a refresh worker

packages/cubejs-backend-shared/src/env.ts:304 keeps refreshWorkerMode on NODE_ENV !== 'production'. Leaving it alone is not neutral once isDevMode() stops reading NODE_ENV: it creates a role combination that was previously unreachable.

With neither NODE_ENV nor CUBEJS_DEV_MODE set (an embedded server-core that supplies driverFactory, so it gets past sanitizeOptions):

before after
isDevMode() true false
scheduledRefreshTimer (OptsHandler.ts:417refreshWorkerMode) true true
configuredForScheduledRefresh() (OptsHandler.ts:554) true true
isRefreshWorker() (OptsHandler.ts:494) false true
isApiWorker() (OptsHandler.ts:505) false false

Before, isDevMode() short-circuited isRefreshWorker() for exactly this config. Now the instance starts the scheduled refresh loop and builds pre-aggregations on a 30s timer (getScheduledRefreshInterval()), against a production pre-agg schema, on a process the operator thinks of as an API server. It is also still not an api worker, so asserOrchestratorOptions (OptsHandler.ts:134) will not catch a rollupOnlyMode: true + CUBEJS_PRE_AGGREGATIONS_BUILDER=true misconfiguration that it would catch on a real API instance.

Either fold refreshWorkerMode into getEnv('devMode') as well (background refresh then defaults off for bare instances — a visible break, but a coherent one), or leave the default as-is and say so in DEPRECATION.md, because "background refresh keeps running" is not what a reader takes away from "dev mode is now off by default".

QueryOrchestrator.ts:55 has the same shape but a milder outcome: the bare instance still picks the in-memory queue/cache driver while now presenting as production.

Medium — the one config this PR targets gets no diagnostic

The warning at env.ts:224-234 fires only when NODE_ENV is set and non-production. The headline case from the PR description — neither variable set — trips no warning at all. What that operator gets instead, from OptsHandler.ts:82, is:

Either CUBEJS_DB_TYPE or CreateOptions.driverFactory must be specified

which says nothing about dev mode, so the upgrade reads as an unrelated driver-config regression. Worth appending a clause to that throw when !this.isDevMode() && process.env.CUBEJS_DEV_MODE === undefined, along the lines of "development mode is no longer enabled implicitly; set CUBEJS_DEV_MODE=true if this instance was relying on it".

Medium — see inline

  • DEPRECATION.md:74 — v1.7.41 is already released (lerna.json is at 1.7.43).
  • DatabricksDriver.ts:471 / OptsHandler.ts:413 — the pre-aggregation schema moves from dev_pre_aggregations to prod_pre_aggregations for the same instances; full rebuild plus orphaned tables, and the migration note does not mention it.

Low

  • env.ts:211-218 and env.ts:244-246 — comment budget / stale pointer (inline).
  • DevServer.ts:54 — banner recommends CUBEJS_DEV_MODE=false where unset is the documented default (inline).
  • No test covers the deprecation warning itself, which is the whole user-facing migration signal. displayCLIWarningOnce keys on a process-global set, and env.test.ts's ignores NODE_ENV case trips NODE_ENV_DEV_MODE on its first getEnv('devMode'), so a later test in the same worker cannot observe it — a test would need to assert on the first call or reset the once-set.

Test/build verification

Not run here — the sandbox refused the chained npx jest invocations, so I am relying on the PR description's reported results rather than reproducing them. The findings above are from reading the files, not from a failing run.

Review threads

No prior threads — the listing returned 0 of my own threads, 0 resolved, 0 re-affirmed. First round on this PR.

· branch `claude/dev-mode-default-behavior-79chik`

Comment thread DEPRECATION.md Outdated
Comment thread packages/cubejs-databricks-jdbc-driver/src/DatabricksDriver.ts
Comment thread packages/cubejs-backend-shared/src/env.ts
Comment thread packages/cubejs-backend-shared/src/env.ts Outdated
@paveltiunov
paveltiunov requested a review from a team as a code owner September 22, 2026 17:54
Comment thread packages/cubejs-server-core/src/core/DevServer.ts Outdated
@github-actions github-actions Bot added the rust Pull requests that update Rust code label Sep 22, 2026
@claude

claude Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @paveltiunov's task in 5m 20s —— View job


Review: 2 new inline findings, 1 cross-cutting question — no blockers beyond the dev-server override

Full review
  • Read the diff and the changed files
  • Swept NODE_ENV / getEnv('devMode') call sites repo-wide for stragglers
  • Reviewed tests and docs
  • Handled prior review threads
  • Posted findings

The core of the change is right: getEnv('devMode') as the single source of truth, off by default, is the correct fix for the unconfigured-instance auth bypass, and the call-site conversions in gateway.ts, sql-server.ts, server.ts, OptsHandler.ts, DatabricksDriver.ts and rust/cubesql/cubesql/src/config/mod.rs:167 are each faithful to what they replaced. DevServer.cubejsToken is still used at DevServer.ts:112, so collapsing the banner leaves no dead binding.

Posted this round

  • packages/cubejs-server/src/command/dev-server.ts:21 — unconditional CUBEJS_DEV_MODE=true overrides an explicit false, which the new banner at DevServer.ts:54 tells users to set; it also flips sql-server.ts:371 skipPasswordCheck to true for cubejs dev-server + CUBEJS_DEV_MODE=false, where master kept the SQL password check on. And it runs before dotenv.config(), so a .env value is invisible either way.
  • packages/cubejs-backend-shared/src/env.ts:231 — the deprecation warning tells anyone with NODE_ENV=staging or NODE_ENV=test (Jest sets it) to set CUBEJS_DEV_MODE=true, i.e. advises correctly-configured instances to enable the auth bypass. It should describe, not prescribe.

On the two deliberately-deferred NODE_ENV defaults

Fold in detectQueueAndCacheDriver (packages/cubejs-query-orchestrator/src/orchestrator/QueryOrchestrator.ts:55). After this PR an embedded server-core with CUBEJS_DB_TYPE set and no NODE_ENV is production by every dev-mode criterion — JWT enforced, prod logger, prod_pre_aggregations, redaction on — yet still gets cacheAndQueueDriver: 'memory', so a multi-replica deployment silently runs without a shared queue. That incoherence is new: before the change the same instance was dev mode throughout. Aligning it on !getEnv('devMode') is the smaller surprise than leaving it.

refreshWorkerMode (env.ts:304) is less urgent — startScheduledRefreshTimer (server.ts:379) only consults configuredForScheduledRefresh(), so background refresh ran in dev mode too and nothing flips today. But it is worth a comment at the site saying it is intentionally not the dev-mode decision, otherwise the next pass at this will "align" it and silently stop background refresh for bare setups.

Tests

Coverage of the new devMode matrix in env.test.ts is good. Two gaps: nothing asserts the deprecation warning is actually emitted (nor that it is suppressed once CUBEJS_DEV_MODE is set), which is the one new user-visible behaviour; and env.test.ts:319/afterAll restores process.env.NODE_ENV = nodeEnv, which writes the literal string "undefined" if NODE_ENV was unset — harmless under Jest, which always sets test, but it is the same footgun the file already carries.

I could not run the suites here — yarn/npx jest invocations were blocked by the sandbox in this job — so I am relying on the run reported in the PR description.

Review threads: the listing returned 5 threads of mine, all still live; 0 resolved. 3 of this round's findings were re-affirmed rather than re-posted as duplicates of existing threads — DEPRECATION.md:74 (v1.7.41 vs lerna.json at 1.7.43), the missing pre-aggregation-schema migration note in the DEPRECATION.md section (embedders with NODE_ENV unset silently move from dev_pre_aggregations to prod_pre_aggregations via OptsHandler.ts:412 and DatabricksDriver.ts:471), and the 7-line JSDoc at env.ts:211. A fourth open thread at env.ts:246 (the logRedaction comment still pointing at OptsHandler.isDevMode, with isDevMode now a bare alias for devMode()) also still applies.

· branch `claude/dev-mode-default-behavior-79chik`

Comment thread packages/cubejs-server/src/command/dev-server.ts Outdated
Comment thread packages/cubejs-backend-shared/src/env.ts Outdated
@claude

claude Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @paveltiunov's task in 4m 46s —— View job


Review: 2 new findings (1 high, 1 medium), 2 prior threads re-affirmed

Full review
  • Read the diff and the changed files
  • Trace the remaining NODE_ENV usages across packages, docker images and Rust
  • Check tests and docs
  • Handle prior review threads
  • Post findings

Verdict

The direction is right and the sweep is thorough — after this change the only NODE_ENV reads left in packages/*/src are the two the description names as deliberate (env.ts:295 refreshWorkerMode, QueryOrchestrator.ts:55) plus the container.ts compatibility shim and gracefulShutdown. The Rust side no longer keeps its own copy of the rule. Docs and DEPRECATION.md are consistent with the new behaviour.

Two things the sweep missed.

1. packages/cubejs-api-gateway/src/gateway.ts:288 — the devServer CreateOption no longer disables auth (high). enforceSecurityChecks now reads the env var, but the playground is mounted off options.devServer (server.ts:455). An embedder passing devServer: true without CUBEJS_DEV_MODE gets the playground, a randomly generated apiSecret (OptsHandler.ts:445), the 🔓 Authentication checks are disabled banner — and JWT enforcement on every request. Before this PR the two agreed, because NODE_ENV === 'production' was false there. Same divergence at gateway.ts:394 for GraphiQL. Fix needs ?? rather than || plus enforceSecurityChecks: !this.options.devServer from server-core; details inline.

2. DEPRECATION.md:495 — the migration note understates the impact (medium). An instance that was implicitly in dev mode also loses the default cubestore external DB and the auto-started Cube Store (OptsHandler.ts:310, :348), and at the same time becomes a refresh worker, because isRefreshWorker() is !isDevMode() && configuredForScheduledRefresh() (OptsHandler.ts:494) while refreshWorkerMode still defaults to NODE_ENV !== 'production' (env.ts:295). Net effect on first start: a background rebuild of every pre-aggregation into prod_pre_aggregations in the source database, on every replica. devMode=false + refreshWorker=on is a combination that could not occur before this PR — a reason to fold refreshWorkerMode in rather than leave it.

Checked and fine

  • dev-server.ts setting CUBEJS_DEV_MODE=true before dotenv runs matches the old NODE_ENV=development behaviour — no regression for .env-supplied values.
  • Docker: latest*.Dockerfile ship NODE_ENV=production and cubejs server forces it anyway, so dev mode there already required the flag. dev.Dockerfile/testing-drivers.Dockerfile set NODE_ENV=development but run cubejs server, so they were not relying on implicit dev mode either.
  • Rust: dev_mode in config/mod.rs feeds only log_redaction:222; nothing else changes.
  • Tests: the three server-core tests that now set CUBEJS_DEV_MODE=true are the right three, and the rewritten log-redaction expectations line up with the new default. The deprecation warning itself is untested — minor, not raised inline.

Not verified

I could not run the test suites from this environment (the sandbox declined the jest invocations), so the results in the description are taken as reported.

Review threads

The listing returned 2 of my own threads; 0 resolved, 2 re-affirmed as still live (dev-server.ts:21 unconditional CUBEJS_DEV_MODE=true plus the DevServer.ts:54 wording, and the env.ts:227 warning text). A third finding this round — the DevServer.ts:54 banner being inert under cubejs dev-server — was dropped inline as a duplicate of the first of those.

· branch `claude/dev-mode-default-behavior-79chik`

Comment thread packages/cubejs-api-gateway/src/gateway.ts Outdated
Comment thread DEPRECATION.md
@claude

claude Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @paveltiunov's task in 5m 48s —— View job


1 medium, 2 low — the security tightening is right and the CLI/Docker paths check out; the deprecation warning misses the one population it exists for.

Review details
  • Read the full diff
  • Review env.ts / dev mode resolution
  • Review ServerContainer + dev-server command ordering
  • Review gateway, sql-server, Databricks, Rust
  • Review docs & tests
  • Handle prior review threads
  • Post findings

Findings posted inline

Sev Where Concern
Medium packages/cubejs-backend-shared/src/env.ts:222 Warning gated on NODE_ENV !== undefined, so the neither-set instance — the PR's headline case — flips out of dev mode with no runtime signal
Low packages/cubejs-backend-shared/src/env.ts:297 Stale // It's true by default for development duplicating the new comment above it
Low packages/cubejs-backend-shared/test/log-redaction.test.ts:237 afterEach writes the literal "undefined" back into NODE_ENV; restoreNodeEnv in the sibling file exists for this

What I verified and found sound

  • CLI/Docker unaffected. latest.Dockerfile:42 sets NODE_ENV=production and command/server.ts:19 forces it, so neither the images nor cubejs server change behaviour or see the warning.
  • The dev-server fix from the last round holds. Defaulting CUBEJS_DEV_MODE in container.ts:254 after dotenv.config() means an explicit value from either the environment or .env wins, and NODE_ENV is still synced at container.ts:262 before cube.js/cube.py is loaded, so user config code reading NODE_ENV is unchanged. runProjectDiagnostics() reads neither, so moving the assignment out of dev-server.ts is safe.
  • cubejs generate builds a ServerContainer without devMode (cubejs-cli/src/command/generate.ts:55), so it now runs outside dev mode and hits the driverFactory/dbType/apiSecret validation at OptsHandler.ts:454. Scaffolded projects are fine — templates.ts:14 writes CUBEJS_DEV_MODE=true into .env, which lookupConfiguration loads first.
  • isPreAggsBuilder doesn't regress. For the newly-non-dev bare instance, isDevMode() goes false but refreshWorkerMode is still NODE_ENV !== 'production', so isRefreshWorker() becomes true and OptsHandler.ts:516 still returns true.
  • Rust. dev_mode feeds only log_redaction (config/mod.rs:222), and no other NODE_ENV read remains under rust/.
  • SQL API. sql-server.ts:345/:371 were already on getEnv('devMode'); only the logger at :53 moved, and it lands on the same value for cubejs dev-server as before.
  • Docs read accurately against the new behaviour, and v1.7.44 matches lerna.json at 1.7.43.

Not verified

Could not run the test suites — jest invocations were blocked by the sandbox in this run. The findings above are static.

Review threads

The listing returned 2 of my own threads; 0 resolved, 2 re-affirmed as still live:

  • gateway.ts:288options.enforceSecurityChecks || !getEnv('devMode') still swallows an explicit false, and server.ts still doesn't pass the option down.
  • DEPRECATION.md:495 — the migration note still covers only the pre-aggregation schema rename, not the Cube Store default or the refresh-worker flip.
· [`claude/dev-mode-default-behavior-79chik`](https://github.com/cube-js/cube/tree/claude/dev-mode-default-behavior-79chik)

Comment thread packages/cubejs-backend-shared/src/env.ts
Comment thread packages/cubejs-backend-shared/src/env.ts Outdated
Comment thread packages/cubejs-backend-shared/test/log-redaction.test.ts
@codecov

codecov Bot commented Sep 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.80000% with 34 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.60%. Comparing base (3222ec0) to head (ed49855).
⚠️ Report is 4 commits behind head on master.

Files with missing lines Patch % Lines
rust/cubesql/cubesql/src/config/mod.rs 23.80% 16 Missing ⚠️
packages/cubejs-api-gateway/src/gateway.ts 50.00% 3 Missing and 4 partials ⚠️
packages/cubejs-server/src/bin/dev-server.ts 0.00% 5 Missing ⚠️
packages/cubejs-server/src/server/container.ts 91.30% 2 Missing ⚠️
packages/cubejs-api-gateway/src/sql-server.ts 0.00% 0 Missing and 1 partial ⚠️
packages/cubejs-backend-shared/src/env.ts 97.67% 1 Missing ⚠️
...ejs-databricks-jdbc-driver/src/DatabricksDriver.ts 0.00% 1 Missing ⚠️
packages/cubejs-server-core/src/core/DevServer.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #11959      +/-   ##
==========================================
+ Coverage   80.52%   80.60%   +0.08%     
==========================================
  Files         508      508              
  Lines      107359   107445      +86     
  Branches     4043     4072      +29     
==========================================
+ Hits        86450    86608     +158     
+ Misses      20337    20259      -78     
- Partials      572      578       +6     
Flag Coverage Δ
cube-backend 61.74% <82.69%> (+0.50%) ⬆️
cubesql 84.89% <23.80%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Member Author

On the item the last review listed under "Considered and not posted" — CUBEJS_LOG_REDACTION=1 skipping the dev-mode override the same way the empty value used to: you were right to raise it and I don't think the adjudication you applied covers it. Fixed in 2a6328a.

My guard asked whether the variable was non-blank. cubesql's env_parse_bool honours only true and false, so 1 counted as a choice here while Config::default() had already warned about it and fallen back on CUBEJS_DEV_MODE — I'd fixed one spelling of the bug (empty) and left every other unparseable one. Asking for the spellings that are actually honoured covers all of them at once:

// The spellings cubesql's env_parse_bool honours. Anything else - empty,
// `1`, a typo - it warned about and fell back on, so it chose nothing
let redaction_chosen = env::var("CUBEJS_LOG_REDACTION")
    .is_ok_and(|v| matches!(v.trim().to_lowercase().as_str(), "true" | "false"));

The "parsing it again would warn twice" reasoning you cite as adjudicating it is about not re-parsing, which this still doesn't do — it only asks whether a usable value is there. The two are separable, and conflating them is what left the half-fix in place.

The reachability caveat you noted stands and is unchanged: a server-core process throws in asBoolStrict() before the SQL API starts, so this only reaches an embedder calling registerInterface directly.

Same push takes your wording for the block comment, per my reply on that thread — it keeps the sentence saying why the override exists, which mine had dropped.

cargo fmt --all -- --check and cargo clippy --locked --all-targets -- -D warnings clean.


Generated by Claude Code

@claude

claude Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

Claude finished @paveltiunov's task in 7m 37s —— View job


Review: no blocking issues — 1 medium, 3 low, posted inline

Full review
  • Fetch the diff and read the changed files
  • Review dev-mode resolution (env.ts, OptsHandler, gateway, container)
  • Review the native/Rust log-redaction threading
  • Review the pre-aggregation schema pin
  • Review tests and docs
  • Resolve stale review threads, dedupe findings
  • Post inline comments and final summary

Findings

# Sev Where Concern
1 medium DEPRECATION.md:555 The devServer: false + CUBEJS_DEV_MODE=true paragraph omits that pgSqlPort still defaults to 15432 and skipPasswordCheck is still true, so that config now presents as JWT-enforced while the SQL API accepts any credentials
2 low packages/cubejs-server-core/test/unit/index.test.ts:183 beforeEach never drops the new schema pin; cores here are never shut down, so CUBEJS_PRE_AGGREGATIONS_SCHEMA leaks into the next test file, where it reads as a user-set value
3 low packages/cubejs-backend-native/src/config.rs:147 redaction_chosen re-implements env_parse_bool's accepted spellings; widening the cubesql side would silently overwrite a value cubesql honours
4 low packages/cubejs-server/test/container.test.js:144 Comment names OptsHandler as the pinner; the pin is taken at the end of CubejsServerCore's constructor

What I checked and found sound

Dev-mode resolution is single-sourced. OptsHandler.isDevMode() (:489), ApiGateway (gateway.ts:292), CubejsServerCore (server.ts:205) and SQLServer (sql-server.ts:53) all compute devServer ?? getEnv('devMode') from the same CreateOptions, and OptsHandler spreads ...opts last so devServer: this.isDevMode() and the caller's value cannot disagree. All ten former getEnv('devMode') reads in the gateway now go through this.devServer; filterVisibleItemsInMeta correctly keeps a local because visibilityFilter is a plain function.

enforceSecurityChecks is back on ||. server.ts:492 never passes the key, so !this.devServer is reached either way, and an explicit false can't disable auth outside dev mode — matching master. The four cases in index.test.ts pin both directions.

cubejs dev-server is byte-identical to master on the SQL API axis. Asking through CreateOptions.devServer rather than writing CUBEJS_DEV_MODE leaves pgSqlPort undefined and skipPasswordCheck false. Verified through CubejsServer's constructor (packages/cubejs-server/src/server.ts:67) and the container.test.js matrix, which asserts on getEnv('pgSqlPort')/getEnv('devMode') rather than on which variable got written.

The NODE_ENV take-back is correctly scoped. wroteNodeEnv gates it, the restore only fires when the value is still the development this run wrote, and nodeEnv === undefined deletes rather than storing the string "undefined". Three tests cover the cube.js-chose-its-own, cube.js-chose-the-same, and never-wrote cases.

The schema pin holds up under the concurrency it claims to. Symbol-keyed shares mean a repeated shutdown() can't spend another instance's share, a drop invalidates outstanding shares so a late release can't spend the next pin's, and the empty-string case (CUBEJS_PRE_AGGREGATIONS_SCHEMA= from a .env) pins over rather than treating it as set. CubejsServer.shutdown does reach core.shutdown() (packages/cubejs-server/src/server.ts:268), so the release path is live, and the container's dropPreAggregationsSchemaPin() runs after the old instance is down and before dotenv.config({override: true}), so a user's .env value is restored rather than lost. Taking the pin as the constructor's last statement is covered by two throw-path tests.

refreshWorkerMode and detectQueueAndCacheDriver deliberately left on NODE_ENV, both commented at the site. Correct call — aligning detectQueueAndCacheDriver would make cacheAndQueueDriver: 'cubestore' throw cubeStoreDriverFactory is a required option at startup for anyone without Cube Store.

Docs. NODE_ENV no longer appears in docs-mintlify/ except as the deliberate deprecation mentions. The warning snippets now lead with CUBEJS_DEV_MODE=false and separate "overrides the commands" from "does not override devServer", which matches isDevMode()'s ??. v1.7.44 is right — lerna.json is at 1.7.43.

Not verified

No node_modules in this checkout, so yarn tsc, yarn lint, the Jest suites and cargo were not run here. The findings above are from reading the working tree; the PR body's own run results stand unchallenged by anything I saw.

Review threads

The listing returned 0 unresolved threads (both pages empty), so none of mine were resolved and none re-affirmed. All four findings above are new and were posted inline.

· branch [`claude/dev-mode-default-behavior-79chik`](https://github.com/cube-js/cube/tree/claude/dev-mode-default-behavior-79chik)

Comment thread packages/cubejs-server-core/test/unit/index.test.ts
Comment thread packages/cubejs-server/test/container.test.js Outdated
Comment thread packages/cubejs-backend-native/src/config.rs Outdated
Comment thread DEPRECATION.md
The cores built there take the pin in their constructor and are never shut down,
so the first case with CUBEJS_DEV_MODE=true left dev_pre_aggregations set for the
rest of the file. Every later case resolving its own schema then hit the conflict
branch, printing the "already set for this process" warning into the suite output
- one occurrence before this, none after. Behaviour was unaffected, since
userPreAggregationsSchema() hides a value this process pinned, but a warning that
fires because of test bookkeeping is one nobody will read when it means something.

Uses the two lines OptsHandler.test.ts already has.

Also corrects a comment in container.test.js that still named OptsHandler as the
site that pins. It has not been since the pin moved to the end of the
CubejsServerCore constructor, which is what two of the OptsHandler tests are about.

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

claude Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

Review in progress

  • Gather context (diff, changed files)
  • Review the new pre-aggregation schema pin in server-core
  • Review the native / cubesql log-redaction changes
  • Review env/gateway/container dev-mode resolution
  • Reconcile prior review threads
  • Post findings

View job run

The native guard re-matched `true`/`false` itself to decide whether
CUBEJS_LOG_REDACTION was chosen, while ConfigObjImpl decides the same thing by
parsing it. Two copies that are only correct while they agree: widen the parse on
the cubesql side and a value it honours would read as unchosen here, so
`c.log_redaction = !dev_mode` would overwrite a preference the user did express -
quietly, since cubesql's own parse succeeded.

cubesql now has one parse_bool behind both env_parse_bool and a new
env_bool_is_set, and the bridge calls the latter. The `pub` is on a function that
has an external caller, which is what the earlier round objected to about
env_parse_bool.

Also documents what `devServer: false` with CUBEJS_DEV_MODE=true does to the SQL
API. That paragraph listed the Cube Store loss and the schema move but not the
half with security consequences: the SQL API keys off the variable alone, so the
Postgres endpoint still comes up on 15432 accepting any credentials while the REST
and GraphQL APIs now enforce JWT. Before this change the instance was in
development mode throughout and the open SQL API matched an equally open HTTP API;
now it presents as authenticated while that port is not.

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

claude Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

Claude finished @paveltiunov's task in 8m 4s —— View job


Review round complete — no correctness defects found; 3 low findings posted inline (0 high, 0 medium, 3 low)

Full review
  • Gather context (diff, changed files)
  • Review core dev-mode resolution (env.ts, OptsHandler, gateway.ts, container.ts)
  • Review Rust / native bridge changes
  • Review the pre-aggregation schema pin
  • Review tests and docs
  • Resolve stale review threads / dedupe findings
  • Post findings

Findings

# Severity Site Concern
1 low packages/cubejs-backend-shared/src/env.ts:296 The pin-conflict warning tells a single-instance user to "run one Cube instance per process". CUBEJS_PRE_AGGREGATIONS_SCHEMA=my_schema plus preAggregationsSchema: 'analytics_preaggs' in cube.js reaches that branch on the first and only core, where the advice is unactionable. pinnedPreAggregationsSchema !== undefined already distinguishes the two cases.
2 low packages/cubejs-server/src/server/container.ts:284 lookupConfiguration carries 14 comment lines against 25 code lines; three of the five blocks restate the statement beneath them.
3 low packages/cubejs-server-core/src/core/server.ts:158 The heldPreAggregationsSchemaPin JSDoc re-argues release-by-identity, which is already argued at env.ts:245-246 and server.ts:1019-1020.

Considered and not posted

  • sql-server.ts:355 keeps the SQL API password check on getEnv('devMode'), so devServer: false + CUBEJS_DEV_MODE=true leaves the Postgres endpoint on 15432 accepting any credentials while REST and GraphQL now enforce JWT. This is the one auth asymmetry the change introduces and it is the right call for the opposite direction (a devServer: true embedder must not silently lose the password check). de48e38 documents it in DEPRECATION.md with both remedies. Adequately handled.
  • DatabricksDriver.ts:471-473 — "It pins CUBEJS_PRE_AGGREGATIONS_SCHEMA above" has an ambiguous subject and "above" points at nothing in that file. Not worth a round on its own.

What I verified

  • The NODE_ENV sweep is complete: no read of NODE_ENV or getEnv('nodeEnv') survives as a dev-mode proxy anywhere in packages/*/src. The two deliberate survivors (refreshWorkerMode at env.ts:409, detectQueueAndCacheDriver at QueryOrchestrator.ts:58) are both commented as such, and container.ts:394 gracefulShutdown still reads the synced value, matching master on every path.
  • Dev mode resolves identically in all four places that decide it — OptsHandler.isDevMode(), ApiGateway, CubejsServerCore's logger, and SQLServer — each as devServer ?? getEnv('devMode'). All ten former getEnv('devMode') sites in gateway.ts now read this.devServer; enforceSecurityChecks correctly kept || so an explicit false cannot disable auth.
  • The pin's lifecycle holds under the cases that matter: taken last in the constructor (so a throw takes none), released by identity on shutdown (double shutdown is a no-op), dropped outright before dotenv on a SIGUSR1 reload, and skipped for a per-tenant preAggregationsSchema function. hasPreAggregationsEnvVars excludes CUBEJS_PRE_AGGREGATIONS_SCHEMA for the default data source and matches only the CUBEJS_DS_* prefix otherwise, so the written variable cannot be mistaken for pre-aggregation credentials. The write lands before any async continuation, so no driver can read the variable ahead of it.
  • Rust: parse_bool is now the single definition behind both env_parse_bool and env_bool_is_set, and the native override at config.rs:142 moves only the default, matching ConfigObjImpl's env_parse_bool("CUBEJS_LOG_REDACTION", !dev_mode) precedence. dev_mode has no other consumer in ConfigObjImpl, so dropping the NODE_ENV term changes nothing else.
  • DEPRECATION.md claims check out against the code: the cubejs server / Docker NODE_ENV sync, the devServer: true under NODE_ENV=production auth flip, the CreateOptions.preAggregationsSchema pin precedence, and v1.7.44 against lerna.json at 1.7.43. The docs sweep is clean — the remaining NODE_ENV mentions are all the deliberate "no longer has any say" ones.

Review threads

The listing returned 0 of my own unresolved threads (paged to the end, both pages empty of unresolved nodes) — nothing to resolve and nothing to re-affirm. All three findings above are new.

Not run

node_modules is absent in this checkout, so I did not run yarn tsc, yarn lint or any Jest suite; the findings come from reading the files at HEAD. CI's unit, lint and Check fmt/clippy jobs remain the signal for those.

· branch `claude/dev-mode-default-behavior-79chik`

Copy link
Copy Markdown
Member Author

CI triage: unit-core on 2a6328a (job) — not this PR's. Infrastructure, in the test fixture's container setup.

Five cubesqlplanner tests failed (test_diamond_join_over_direct_path_sql, test_diamond_join_over_view_sql, test_measure_switch_cross_join, test_multi_fact_two_measures_from_different_cubes, test_segment_as_dimension_in_pre_aggregation_query), all with the same panic at the same line:

panicked at cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/pg_service.rs:67:10:
Failed to install postgresql-16-hll: Exec(ExitCodeMismatch { expected: 0, actual: 100 })

That line is init_pg running apt-get update -qq && apt-get install -y -qq postgresql-16-hll inside the Postgres testcontainer. apt exit 100 is a fetch/resolution failure — the container could not reach the Debian repos. init_pg sits behind a OnceCell, so one failed install fails every test that needs Postgres, which is exactly the set above; no SQL was generated or compared.

Why it isn't the diff: git diff origin/master...HEAD -- rust/cube/cubesqlplanner is empty. The only Rust file this PR touches is rust/cubesql/cubesql/src/config/mod.rs, and the change there is extracting parse_bool out of env_parse_bool plus adding env_bool_is_set — nothing reachable from SQL planning, and nothing reachable from apt.

I ran those five tests here and they pass, but I'll be precise about what that does and doesn't show: try_execute_pg_from_options compiles to None without the integration-postgres feature (test_context.rs:961), and unit-core enables it via --features cubesqlplanner/integration-cubestore. Docker isn't available in this container, so I could not exercise the container path locally — my run covers the SQL generation, not the fixture. The panic message is what settles it either way.

Not spending the re-run on 2a6328a: the head is now de48e38 and its own unit-core re-runs this. I'll report if it fails there too.


Generated by Claude Code

Comment thread packages/cubejs-backend-shared/src/env.ts Outdated
Comment thread packages/cubejs-server/src/server/container.ts Outdated
Comment thread packages/cubejs-server-core/src/core/server.ts
The message ended with "run one Cube instance per process", which is no help to
the likeliest way of reaching it: one instance, a CUBEJS_PRE_AGGREGATIONS_SCHEMA
the user set, and a CreateOptions.preAggregationsSchema that overrules it through
`...opts`. There is no second instance to separate, and the other half of the
advice - align the variable - names the wrong fix, since dropping either setting
works.

pinnedPreAggregationsSchema being set is exactly "this process pinned it for an
earlier instance", so the two cases are one condition apart. Each now ends with
the remedy that applies to it.

Both branches are on the tests that already cover them, asserting the advice they
get and that they do not get the other one.

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

claude Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

Review in progress

  • Read the changed files
  • Analyse dev-mode resolution, auth, pre-agg schema pin, Rust config
  • Reconcile with prior review threads
  • Post findings

View job run

Three blocks restated what the line under them says or what another site already
argues. In lookupConfiguration the "measures the user's own config" sentence is
the two statements below it read aloud, and "kept in sync for user config code"
repeats the devServer block three lines up; the take-back's second line described
its own condition. The field JSDoc in server-core re-argued release-by-identity,
which env.ts and shutdown() each state at the point they rely on it - three copies
of one rationale, stale together the day it changes.

What is left at each site is the part that is not recoverable from the code: the
ordering constraint, that `cube.js` sees NODE_ENV, the scoping of the take-back,
and what the field holds.

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

claude Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

Review in progress

  • Read the changed files
  • Analyze dev-mode resolution, the schema pin, and the Rust/native path
  • Reconcile prior review threads
  • Post findings

View job run · branch claude/dev-mode-default-behavior-79chik

…NODE_ENV

The previous commit cut that note from container.ts along with the restatement
around it, which left the limitation recorded nowhere: a `cube.js` that assigns
`development` itself cannot be told apart from this run's own write, so the
take-back claims it. Without that, this test reads as asserting an arbitrary
choice, and it is the case someone would try to "fix".

It belongs on the test rather than back in the source, where the line it sat on
now says only what it scopes.

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

claude Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

Claude finished @paveltiunov's task in 7m 20s —— View job


No functional defects this round — 0 high, 0 medium, 4 low (all comment/test hygiene)

Review detail
  • Fetch the diff for this round
  • Read the changed source files, not just the hunks
  • Trace dev-mode resolution across gateway / server-core / OptsHandler / container / native / cubesql
  • Audit the new pre-aggregation schema pin for lifecycle and ordering holes
  • Check test coverage and state isolation
  • Reconcile prior review threads
  • Post inline comments

Threads

The listing returned 0 unresolved threads authored by me (both pages; hasNextPage: false on the second). Nothing to resolve, nothing re-affirmed — every finding from the earlier rounds has been addressed and closed.

What I checked and found clean

Dev-mode resolution agrees everywhere it should. OptsHandler.isDevMode() (packages/cubejs-server-core/src/core/OptsHandler.ts:492), CubejsServerCore's logger (server.ts:206), ApiGateway.devServer (gateway.ts:292) and SQLServer.devServer (sql-server.ts:49) all compute devServer ?? getEnv('devMode'), and server-core passes a real boolean down (server.ts:507), so the gateway's env fallback is unreachable from server-core. All ten former getEnv('devMode') reads in the gateway now go through this.devServer. enforceSecurityChecks is back on ||, which preserves master's semantics — an explicit false cannot disable auth outside dev mode — and the two tests at api-gateway/test/index.test.ts pin both directions.

isRefreshWorker() flipping false → true for a bare instance is still inert. Its only consumer is isPreAggsBuilder() (OptsHandler.ts:524), which was already true via the isDevMode() disjunct and is now true via this one; preAggregationsOptions.externalRefresh at :633 is unchanged. isApiWorker() is false before and after, so asserOrchestratorOptions' rollupOnlyMode check is intact.

The schema pin's ordering holds. pinPreAggregationsSchema runs last in the CubejsServerCore constructor (server.ts:374), and the only consumer — DatabricksDriver.getPreAggrSchemaName() — is reached from query() at :450, i.e. per statement, long after construction. startScheduledRefreshTimer() at :258 precedes the pin but only arms timers, so no build can read the variable early. Holder identity via Symbol rather than a count correctly survives the repeated-shutdown() and drop-under-a-live-holder cases, and both are tested (OptsHandler.test.ts, env.test.ts).

The pin does not leak into anything else that scans the environment. hasPreAggregationsEnvVars excludes CUBEJS_PRE_AGGREGATIONS_SCHEMA explicitly (env.ts:154), and configuredForQueryProcessing() scans only CUBEJS_DB*/CUBEJS_AWS* (OptsHandler.ts:543). getEnv('preAggregationsSchema') has exactly one consumer repo-wide, the Databricks driver, so the blast radius is what the comment claims.

The NODE_ENV take-back in container.ts:285-304 is correctly scoped to the value this run wrote, and the four cases that matter — cube.js choosing devServer: false, choosing its own NODE_ENV, choosing development itself, and a run that never wrote it — are each covered in container.test.js. gracefulShutdown's NODE_ENV === 'production' ? 30 : 2 resolves identically to master on every CLI path I traced.

Rust. dev_mode feeds only log_redaction in ConfigObjImpl (rust/cubesql/cubesql/src/config/mod.rs:222), so dropping the NODE_ENV term has no other reach. parse_bool / env_bool_is_set / env_parse_bool now share one definition of the honoured spellings, which closes the CUBEJS_LOG_REDACTION=1 gap from the last round, and config.rs:141 moves only the default — an explicit value still wins on both sides.

DEPRECATION.md matches the code as it now stands, including the cubejs-dev-server bin being the one path that does not sync NODE_ENV, and v1.7.44 is the right next version against lerna.json's 1.7.43.

Findings

# Site Concern
1 packages/cubejs-server/test/container.test.js:296-301 Two comment paragraphs state the same idea; the second arrived in 0de0215 without the first being removed
2 packages/cubejs-server/test/container.test.js:215 stubConfigurationFile({}) is called twice in one test, and line 229 rebinds over both
3 packages/cubejs-server/test/container.test.js:11-14 The lookupConfiguration paragraph documents a helper 29 lines below it, stacked on TestServerContainer's own explanation
4 packages/cubejs-backend-shared/src/env.ts:289-300 Two blocks restate the remedy ternary and the warning key; the one non-deducible sentence is that displayCLIWarningOnce keys per message

Not verified

node_modules is absent in this checkout and installing the monorepo is not something I ran, so I did not execute yarn tsc, yarn lint or any Jest suite — the reasoning above is from reading the sources. The PR's own CI is the signal for those.

· [`claude/dev-mode-default-behavior-79chik`](https://github.com/cube-js/cube/tree/claude/dev-mode-default-behavior-79chik)

Comment thread packages/cubejs-server/test/container.test.js Outdated
Comment thread packages/cubejs-server/test/container.test.js Outdated
Comment thread packages/cubejs-server/test/container.test.js Outdated
Comment thread packages/cubejs-backend-shared/src/env.ts Outdated
The previous commit added a note that the test already carried in different
words, leaving two blocks stacked above it saying the same thing. My check for
whether the point survived an earlier trim grepped for one word rather than the
idea, so it came back empty and I restored something that was never gone.

Merged into the one sentence that is load-bearing: the two writes are
indistinguishable, and the config settles which one wins. Which readers still
consult NODE_ENV belongs to refreshWorkerMode and detectQueueAndCacheDriver,
where it is already said, not to this test.

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

claude Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

Review in progress

  • Fetch base and read the diff
  • Review changed source files
  • Check prior review threads
  • Post findings

View job run

…hey belong

Batched, so the CI matrix restarts once rather than four times.

- container.test.js dropped a `stubConfigurationFile({})` that the test overwrites
  twelve lines later with the same argument before rebinding the loader over it.
  Nothing asserted depended on it.
- The header comment about `lookupConfiguration` sat above `TestServerContainer`,
  which the paragraph under it explains. Moved to the helper it describes, so a
  reader at the class is not handed two unrelated justifications.
- env.ts said twice, above the value and above the ternary, what the `remedy`
  branches name themselves. Only the keying survives, at the call it explains: it
  is per message, so a constant key lets one instance silence the next.
- The DatabricksDriver fallback said "It pins CUBEJS_PRE_AGGREGATIONS_SCHEMA
  above" - an ambiguous subject pointing at another package. Names server-core as
  the writer and the branch above as the reader.

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

claude Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

Claude finished @paveltiunov's task in 5m 54s —— View job


Review complete — no findings. 0 high, 0 medium, 0 low.

Details — what was checked, and the notes that did not rise to findings
  • Read the diff and the changed files in full (not just hunks)
  • Traced the four dev-mode resolution sites for agreement
  • Traced the pre-aggregation schema pin lifecycle
  • Checked the Rust / native bridge path
  • Read the new tests and DEPRECATION.md
  • Handled prior review threads
  • Posted the result

Dev mode resolution

All four sites resolve identically — OptsHandler.ts:492 (this.createOptions.devServer ?? getEnv('devMode')), server.ts:206, gateway.ts:292, sql-server.ts:57. OptsHandler.createOptions is a constructor parameter property, so sanitizeOptions at OptsHandler.ts:44 sees it; ...opts is spread last at OptsHandler.ts:425, so options.devServer matches what isDevMode() answered. The gateway's ten consumer sites all read this.devServer (gateway.ts:378, 547, 732, 1590, 1841, 2037, 2523, 2538–2539, 2898–2908, 3047); none still reads the env var.

gateway.ts:296 is ||, not ?? — correct, since server-core never passes enforceSecurityChecks and || preserves master's semantics where an explicit false cannot disable auth outside dev mode.

sql-server.ts:357 and :383 stay on getEnv('devMode'), matching pgSqlPort, so port existence and password generation cannot disagree. DatabricksDriver.ts:475 likewise. Both carry a comment at the site and both are in DEPRECATION.md.

The cubejs dev-server / cubejs-dev-server paths

container.ts:266 computes devServer after dotenv.config(), so a .env CUBEJS_DEV_MODE wins; markDevModeResolvedByCaller() (container.ts:271) suppresses the deprecation warning only on that path. bin/dev-server.ts:10 uses process.env.CUBEJS_DEV_MODE === undefined || getEnv('devMode') — the || order matters and is right: the accessor is only reached when the variable is set, which is also the only case where its warning cannot fire.

The NODE_ENV write/take-back in measureAndApplyDevServer (container.ts:285–304) is scoped to wroteNodeEnv, and all four return paths (:322, :326, :339, plus the cube.ts throw which exits) go through it. devServer is accepted by optionsValidate.ts:74, so new CubejsServer({ devServer }) from the bin validates.

The pre-aggregation schema pin

The lifecycle holds under every ordering I could construct:

  • Pin taken last in the CubejsServerCore constructor (server.ts:374), so a throw above takes none; typeof === 'string' excludes a per-tenant function, covered by OptsHandler.test.ts:419.
  • shutdown() (server.ts:1020) clears the field before releasing, so a second shutdown() cannot spend the share again.
  • Identity-keyed holders (env.ts:224) mean a share invalidated by dropPreAggregationsSchemaPin() releases nothing — verified against the SIGUSR1 flow, where container.ts:473 shuts the old instance down before makeInstance(true) drops and the new core re-pins.
  • userPreAggregationsSchema() (env.ts:314) keeps a pinned value from reading back as a user choice; OptsHandler.ts:293 is the only consumer, and getEnv('preAggregationsSchema') has exactly one remaining reader repo-wide (DatabricksDriver.ts:467).

Two things I looked at and decided were not worth a finding:

  • pinPreAggregationsSchema overwrites an empty-string CUBEJS_PRE_AGGREGATIONS_SCHEMA and dropPreAggregationsSchemaPin then deletes it rather than restoring ''. Every consumer treats empty as absent, so nothing observes the difference — and env.ts:271–273 says so deliberately.
  • dropPreAggregationsSchemaPin() at container.ts:255 invalidates the shares of every core in the process, not just the one being reloaded. Only reachable for someone driving ServerContainer alongside a second CubejsServerCore they built themselves; ServerContainer.start() manages exactly one instance and shuts it down first. The JSDoc at env.ts:226–229 states the semantics.

Rust / native

ConfigObjImpl::default (rust/cubesql/cubesql/src/config/mod.rs:166) is env_parse_bool("CUBEJS_DEV_MODE", false) with a comment saying the Node bridge overrides it. env_bool_is_set (:487) shares parse_bool with env_parse_bool, so the guard at config.rs:145 asks exactly which spellings cubesql honours — 1, empty and typos all read as "not chosen", which is the right answer. dev_mode is read as a JsBoolean at node_export.rs:117 and reaches the factory at :152; sql-server.ts:143 supplies it.

Tests and docs

New coverage is specific rather than restating the code: OptsHandler.test.ts:495 and :524 pin the two orderings that matter (second instance still up; handover after shutdown), container.test.js:213, :244 and :298 pin the three NODE_ENV take-back cases including the indistinguishable one, and env.test.ts:616 covers the warning in both directions plus the markDevModeResolvedByCaller latch with a fresh module registry per case.

DEPRECATION.md covers both devServer directions, the schema move, the Cube Store loss, the log-format change and the SQL API's non-following of devServer: false. The v1.7.44 heading is one ahead of lerna.json's 1.7.43, which is the next release. The docs snippets now lead with CUBEJS_DEV_MODE=false and name both commands and the devServer option, which matches the code.

Not run: node_modules is absent in this checkout and git fetch is not permitted here, so I reviewed against the working tree rather than a computed origin/master...HEAD diff, and did not execute yarn tsc, yarn lint or any suite. The author's reported runs stand unverified by me.

Review threads: the listing returned 0 unresolved threads of mine (both pages came back empty). Nothing to resolve, nothing re-affirmed.

· branch [`claude/dev-mode-default-behavior-79chik`](https://github.com/cube-js/cube/tree/claude/dev-mode-default-behavior-79chik)

This branch was successfully deployed

1 active (outdated) deployment
staging - docs-mintlify df23d6a6 Deployed Sep 22, 2026 by mintlify[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cubejs-jdbc-driver data source driver docs Issues that require a documentation improvement driver:databricks javascript Pull requests that update Javascript code rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants