Repository navigation
fix(cli): os dev compiles the config before it boots, so served connector metadata matches dispatch - #26240
Conversation
…ctor metadata matches dispatch On a host config's os dev boot the served metadata (dev artifact door) and the materialized connectors (the config module) are two copies; an env-derived value differed between them whenever the artifact had been built in another environment. Compile by default (--no-compile keeps the old shortcut) so both come from one evaluation. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cj99V2CpVvyWDjQu1ndoBS
…s a query engine Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cj99V2CpVvyWDjQu1ndoBS
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cj99V2CpVvyWDjQu1ndoBS
📓 Docs Drift CheckThis PR changes 1 package(s): 1 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 a0f194ae8e8af9283612f52005de2871440207b9 && git checkout a0f194ae8e8af9283612f52005de2871440207b9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin df8aac41f2d9d14db04afdfe48511216419dafc0 ac8dadabc591623d4329935c82a3714e4e253ce5 && git checkout -B drift-repro df8aac41f2d9d14db04afdfe48511216419dafc0 && git merge --no-ff ac8dadabc591623d4329935c82a3714e4e253ce5
node scripts/docs-audit/affected-docs.mjs --json df8aac41f2d9d14db04afdfe48511216419dafc0
|
席位核对ACCEPT · The maintainer's standing authorisation for landing reviewed PRs, verbatim: 「按照 pm-dispatch 规则复核过的都授权」.
Generated by Claude Code |
Requested by Leehom · project thread
Fixes #26130
Clause-②: no
Metadata-protocol: none
What was wrong
A host config's
os devboot holds two copies of one stack. The config module, evaluated in theservechild, feeds the AppPlugin wrap; provider-bound connectors are materialized from that registry copy, so it is what dispatch calls. The compileddist/objectstack.jsonfeeds the dev artifact door, and its copy is what/api/v1/meta/connector/NAMEserves.examples/app-showcasebuilds its connectors'baseUrlfromOS_PORTat module evaluation, andos devbooted whatever artifact was already on disk, so the two copies disagreed whenever the artifact had been built in another environment.Measured on
examples/app-showcaseat this branch's base (c7aa9cde),OS_PORT=41302, the artifact compiled with noOS_PORT(baseUrl: http://127.0.0.1:3000), nothing listening on 3000. "dispatch" isPOST /api/v1/automation/showcase_declarative_connector_ping/trigger, whose one node callsshowcase_status_api.requestforGET /api/v1/health.GET /api/v1/meta/connector/showcase_status_api→providerConfig.baseUrlos dev --no-compile(= a bareos devbefore this PR)http://127.0.0.1:3000status: ok(reached 41302)os dev(this PR)http://127.0.0.1:41302status: okos start(production, config joins the boot)http://127.0.0.1:41302status: okos serve objectstack.config.ts(production)http://127.0.0.1:41302status: okos serve --dev objectstack.config.tshttp://127.0.0.1:3000status: ok(reached 41302)os startand productionos servecompose no artifact door, so they serve the module's copy and already agree; they are unchanged.os serve --devhas the same two copies; see Acceptance notes.The fix
Of the two routes the order named, this takes (b): the dev boot compiles instead of reading the artifact as found.
--compileis now on by default, soos devcompilesobjectstack.config.tsinto the artifact it serves, in its own environment, before theservechild starts. Both copies are then one evaluation's answer, and nothing restates which variables a config reads or in what order (no second copy of the port/URL precedence). The watch loop's recompiles already ran in that environment, so the two stay one answer for the session.Route (a), re-resolving env-derived connector config at an artifact boot, has nothing to re-resolve from: the artifact holds the value, not the expression that produced it. Carrying the expression would need a new
packages/specform for env-derived values, which the rulings put out of reach, so it is not proposed.packages/cli/src/commands/dev.ts: the decision is one exported function,devCompilesAtBoot. A pinned artifact (--artifact,OS_ARTIFACT_URL, a remoteOS_ARTIFACT_PATH) is never rebuilt.--no-compilekeeps the old shortcut and the startup staleness warning. A missing artifact is still compiled. With no config, an existing artifact boots as before.os compile(737 KB artifact).content/docs/deployment/cli.mdx: the--compilerow now says default on and names--no-compile. This file is outside the claim's file surface, but the docs row would be false without it.scripts/check-cli-test-child-env.mjs: one census entry for the new test, which spawns the built entry (see Gates).examples/app-showcase/src/system/self-url.ts: header comment only. Itsos devclaim ("follows the live environment") is now true for the served metadata too, and it names the--no-compileexception.Tests
At head
ac8dadabc(the three dev test files rerun there:Test Files 3 passed (3),Tests 15 passed (15); the unit tier and typecheck ran at7f3d5bee, whose only difference from the head is the census line inscripts/check-cli-test-child-env.mjs):packages/cli/test/dev-connector-env-parity.integration.test.ts(integration tier, built entry). It uses a minimal host config with one provider-bound connector whosebaseUrlis built fromOS_PORTthe wayself-url.tsbuilds it, and a host plugin whose provider factory records theproviderConfigit was handed. A prebuilt artifact carries:3000. Oneos devboot withOS_PORT=41302checks that the servedbaseUrland the materialized one both readhttp://127.0.0.1:41302. Result:Tests 2 passed (2).packages/cli/test/dev-compile-at-boot.pin.test.ts(unit tier) checks oclif's parse plusdevCompilesAtBootfor each case: bare,--no-compile, pinned, missing artifact, no config. It passed withdev-no-watch.pin.test.ts.pnpm --filter @objectstack/cli exec vitest run --project unit:Tests 1 failed | 4858 passed. The one failure wasboot-preparation-parity.test.ts, which timed out at 5934 ms against its 5000 ms limit while the gate sweep shared the box. Run alone it answeredTests 37 passed (37). It readsserve.ts, which this PR does not touch.pnpm --filter @objectstack/cli typecheck: exit 0.devCompilesAtBoot's default arm was mutated tofalsethroughscripts/ablation-replace.mjs, the CLI was rebuilt, andablation-dist-preflightfound the marker inpackages/cli/dist/commands/dev.js. The integration test then failed withexpected 'http://127.0.0.1:3000' to be 'http://127.0.0.1:41302'on the served leg, while the materialized leg stayed green, which is the card's defect exactly. The source was restored (blob equals HEAD,git diff HEADempty), the CLI rebuilt, and the preflight--absentcheck passed.Gates
Local subset,
node scripts/pm/dispatch-gates.mjs --commandsatac8dadabc: 120 families derived.--ranreconciliation: 118 run with exit 0, 2 NOT MEASURED, 0 unrun.pnpm --filter @objectstack/spec run check:skill-examples,pnpm check:dual-build-cjs-loads. CI runs both.pnpm check:cli-test-child-envwent red on the first sweep because its census pins the exact set of test files that spawn the builtbin/run.js, and the new integration test joins that set. The census inscripts/check-cli-test-child-env.mjsnow names the file with a one-line reason, the same form as the other members. The gate is green, and its self-test passed all 152 cases. The script's one othergit grephit is a comment inserve-node-env-production-default.e2e.test.ts, which no vitest project selects.node scripts/check-plugin-teardown-shape.mjs --self-testexited 3 on the shallow clone. Aftergit fetch origin 621a4876…(its positive control), it passed 48 cases.node scripts/check-nul-bytes.mjs: exit 0.pnpm exec eslint --no-inline-configon the four touched source files: exit 0. Neither touched package has alintscript.Acceptance notes
os serve --dev objectstack.config.tshas the same disagreement (row 5 above): it composes the dev artifact door and does not compile. This PR does not widen to it, per the ruling. It is reported to the seat.os dev -p 41302with noOS_PORTin the environment:os devforwards the port toserveas--portand exports noOS_PORT, so the showcase's config resolves:3000in both copies. They agree, but dispatch fails withconnector_action(showcase_status_api.request) failed: fetch failed(measured,FLOW_FAILED400).self-url.tssays it follows "the same names, in the same order, that the CLI itself reads to choose the listen port", and the flag is not one of them. This is Showcase REST connectors hard-wirebaseUrltohttp://127.0.0.1:3000, so self-ping flows failfetch failedon any isolated instance — a port mismatch, not an egress block #7538's symptom on the flag path, not this card. It is reported to the seat.os devnow rewritesdist/objectstack.jsonat every start. Anos startthat later boots that file alone serves the values of the last dev boot's environment. That was already true after any watch recompile, andself-url.tsalready says to set the environment for the build.#26088 remains open; this PR does not address it.
Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01Cj99V2CpVvyWDjQu1ndoBS