Repository navigation
fix(cli): a direct os serve --dev compiles the config before its dev artifact door serves it - #26289
Conversation
…artifact door serves it The dev artifact door served dist/objectstack.json as found while dispatch read the evaluated config module, so an env-derived connector baseUrl differed between /meta and what runs. The compile decision os dev reads (devCompilesAtBoot) moves to utils/ so serve reads it too; a supervised serve child keeps its parent's decision. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rDzaJmEJAYLUNJ4kwG2vN
…rve --dev pin gets its own file serve sets NODE_ENV=development on itself, so a compile child spawned from serve.ts inherited it (child-env-source-loader pin). The spawn moves to utils/dev-compile-at-boot.ts as compileForDevBoot, which hands the child the serving environment with the launch NODE_ENV put back, as os dev's compile child sees it. The serve --dev legs leave dev-connector-env-parity (the os dev half, also being reworked for the port flag) for serve-dev-connector-env-parity, with its census entry; the verdicts compare the two copies rather than a port number. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rDzaJmEJAYLUNJ4kwG2vN
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rDzaJmEJAYLUNJ4kwG2vN
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rDzaJmEJAYLUNJ4kwG2vN
📓 Docs Drift CheckThis PR changes 1 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 31 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 0cecbddca1b897a820c318e7bb27487fca267f0e && git checkout 0cecbddca1b897a820c318e7bb27487fca267f0e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin be531f685e7d73ec33aedc2ed67fe997cfc4784d 6f7edd28c330b29e25946d13f28aa3d69435a00f && git checkout -B drift-repro be531f685e7d73ec33aedc2ed67fe997cfc4784d && git merge --no-ff 6f7edd28c330b29e25946d13f28aa3d69435a00f
node scripts/docs-audit/affected-docs.mjs --json be531f685e7d73ec33aedc2ed67fe997cfc4784d
|
席位核对ACCEPT · The maintainer's standing authorisation for landing reviewed PRs, verbatim: 「按照 pm-dispatch 规则复核过的都授权」.
Generated by Claude Code |
Requested by Leehom · project thread
Fixes #26242
Clause-②: no
Metadata-protocol: none
What was wrong
A host config's dev boot holds two copies of one stack. The config module, evaluated in the serving process, feeds the AppPlugin wrap, and provider-bound connectors are materialized from that copy, so it is what dispatch calls. The compiled
dist/objectstack.jsonfeeds the dev artifact door, and its copy is what/api/v1/meta/connector/NAMEserves. #26130 (PR #26240) madeos devcompile before it boots. A directos serve --devstill composed the door over whatever artifact was on disk, so an env-derived value differed between the two copies.Red first on
mainat81bba6c3(before #26240) and re-read on this branch's base3d50a43b:examples/app-showcase, artifact compiled withOS_PORT/PORTunset (all threebaseUrlvalueshttp://127.0.0.1:3000),OS_PORT=41302, nothing listening on 3000. "dispatch" isPOST /api/v1/automation/showcase_declarative_connector_ping/trigger./meta/connector/showcase_status_api→providerConfig.baseUrlbeforeos serve objectstack.config.ts --dev -p 41302http://127.0.0.1:3000http://127.0.0.1:41302status: ok(reached 41302) bothos serve objectstack.config.ts -p 41302(production)http://127.0.0.1:41302http://127.0.0.1:41302, artifact md5 unchangedos dev --no-compile --no-watch -p 41302http://127.0.0.1:3000http://127.0.0.1:3000(unchanged, the operator's shortcut)os dev --no-watch -p 41302:41302since #26240)http://127.0.0.1:41302os serve --devwithOS_ARTIFACT_URL=file://…(pinned)The fix
Route (b), the one #26240 took: the dev boot compiles the config into the artifact it is about to serve, in its own environment, so both copies are one evaluation's answer. Nothing restates which variables a config reads or in what order.
packages/cli/src/utils/dev-compile-at-boot.ts(new):devCompilesAtBoot, moved verbatim fromcommands/dev.tsso both doors read the one decision (dev.tsimportsserve.ts, soserve.tscannot importdev.ts).dev.tsnow imports it; the decision and itsos devbehaviour are unchanged. The#26130pin's import moves with it.packages/cli/src/commands/serve.ts: in the dev artifact door's existing-artifact branch, before the door is constructed, a boot with no supervisingos dev(its channelOS_INTERNAL_ARTIFACT_PATHunset) asksdevCompilesAtBootand compiles. Underos devthe child keeps its parent's decision, soos dev --no-compilestill serves the artifact as built and a defaultos devdoes not compile twice. A boot with no artifact on disk never reaches this branch: no door composes, the wrap is the one copy, and nothing is compiled (so test fixtures and fresh checkouts withoutdist/are untouched). A failed compile refuses the boot with the compile's own output, as underos dev. Production boots never reach the branch (it is inside theisDevdoor block).compileForDevBoot(same util) spawnsos compile CONFIG --output ARTIFACTwith the serving process's environment, including the.envfiles serve loaded, but withNODE_ENVput back to its launch value.serve --devsetsNODE_ENV=developmenton itself; if the child inherited that, it would run under oclif's source loader, whichchild-env-source-loader.pin.test.tsforbids. The child therefore sees whatos dev's compile child sees.Tests
New
packages/cli/test/serve-dev-connector-env-parity.integration.test.ts(integration tier, built entry,-p 0). It uses the cli/dev: on anos devboot withOS_PORTset, served connector metadata reads the build-timebaseUrl(http://127.0.0.1:3000) while dispatch reaches the live port, so/metadisagrees with what runs #26130 fixture, copied so neither file's legs move the other's (fix(cli): os dev -p and os serve --port export the port as OS_PORT/PORT, so a config reads the port the server listens on #26268 is reworking that file for the port flag). The fixture has one provider-bound connector whosebaseUrlis built fromOS_PORT, a provider factory that records the materializedproviderConfig, and a:3000artifact rewritten before each boot. Three legs:os serve --dev: the servedbaseUrlequals the materialized one and is not the build-time one.os dev --no-compile: the served one is still:3000and the artifact's bytes are untouched.os serve: the materialized one is not:3000and the artifact's bytes are untouched.The verdicts compare the two copies, never a port number, so the file holds whether or not fix(cli): os dev -p and os serve --port export the port as OS_PORT/PORT, so a config reads the port the server listens on #26268 has landed. Three runs:
Tests 3 passed (3)at 13.6 s, 13.9 s and 13.2 s. The header declarestest-budget: 20, from the slowest run x 1.25.dev-connector-env-parity.integration.test.ts(cli/dev: on anos devboot withOS_PORTset, served connector metadata reads the build-timebaseUrl(http://127.0.0.1:3000) while dispatch reaches the live port, so/metadisagrees with what runs #26130's file) is unchanged and green at head. Together with the new file:Test Files 2 passed (2),Tests 5 passed (5).Ablation, run once per behaviour through
scripts/ablation-replace.mjs. Each leg rebuilt the CLI, andablation-dist-preflightfound the marker inpackages/cli/dist/commands/serve.jsbefore the run:serve --devleg went red, withexpected 'http://127.0.0.1:3000' to be 'http://127.0.0.1:41302'.--no-compileleg went red, withexpected 'http://127.0.0.1:41302' to be 'http://127.0.0.1:3000'.git diff HEADempty. After a rebuild,preflight --absentpassed for both markers.pnpm --filter @objectstack/cli exec vitest run --project unitatca2e4216:Test Files 334 passed (334),Tests 4878 passed (4878). The later commits change one refusal string and one test header.pnpm --filter @objectstack/cli exec vitest run --project integrationat378de620(the first head; the later diff moves the compile spawn into the util and splits the pin out):Test Files 173 passed | 1 skipped (174),Tests 1477 passed | 9 skipped. This tier was run because the diff touches the serve boot path, and every direct-dev fixture that has adist/now compiles at boot.pnpm --filter @objectstack/cli typecheck: exit 0 at head.Gates
Local subset at head
6f7edd28c:node scripts/pm/dispatch-gates.mjs --commandsderived 91 families, and each was run with its exit code captured before any pipe.--ranreconciliation:91 run, 0 NOT-MEASURED, all exit 0.ca2e4216:pnpm check:doc-authoringwent red on a real finding: the compile refusal's runtime text carried the tracker id. It was stripped (751f30aa) and the gate is green.check-plugin-teardown-shape --self-testexited 3 on the shallow clone. Aftergit fetch origin 621a4876it exited 0 (48 cases).check:dual-build-cjs-loadsexited 3 there, with its prerequisite not met. It exited 0 on the final sweep.pnpm check:cli-test-child-env: green with the new census entry, and its self-test passed 152 cases. The first attempt was refused because the leg env's spread came afterNODE_ENV;NODE_ENV: undefinednow comes last.eslint --no-inline-configon the six touched source and test files: exit 0.node scripts/check-nul-bytes.mjs: exit 0.Acceptance notes
-pport flag; it touchesdev.ts,serve.tsand cli/dev: on anos devboot withOS_PORTset, served connector metadata reads the build-timebaseUrl(http://127.0.0.1:3000) while dispatch reaches the live port, so/metadisagrees with what runs #26130's test file), feat(spec,metadata-protocol,service-storage,runtime,cli)!: a seed record can carry a file the package ships, seedFile(path) #26244 and fix(cli,i18n,console): the hosted product reads "ObjectOS Cloud" everywhere; the Community Console carries a "Powered by ObjectStack" mark #26215 (serve.ts, other regions). A trial merge of fix(cli): os dev -p and os serve --port export the port as OS_PORT/PORT, so a config reads the port the server listens on #26268 into this head is clean. On the merged tree with the CLI rebuilt, both parity files answerTests 6 passed (6); there-p 0makes the config read:0, and the relative verdicts still hold. Whichever of the two lands second mergesmain.os serve --devbullet incontent/docs/deployment/cli.mdxsays nothing about the artifact, so it is not false.os serve --devnever reaches this door: its standalone stack serves metadata and dispatch from the same artifact bundle, so the two copies already agree, both at the build-time value. That is the same posture asos start --artifact. It was observed only, not measured on a fixture, and is not this card.NODE_ENV=development os serve(no--dev) is a dev boot too (isDevelopmentBoot), composes the same door, and so compiles the same way.os startwithNODE_ENV=developmentis supervised and does not.🤖 Generated with Claude Code
https://claude.ai/code/session_019rDzaJmEJAYLUNJ4kwG2vN