Repository navigation
fix(cli): doctor's Environment files row names os serve / os start as its loaders in the source posture (#22249) - #22417
Conversation
… its loaders in the source posture In the source checkout `os dev` serves, the `Environment files` row now says whose `.env*` cascade it resolved: `os serve` / `os start`. The cascade itself (doctorNodeEnv, NODE_ENV || 'production') and every env-derived verdict are unchanged; outside that posture the row is the same bytes as before. The posture is asked once in run() and handed to both the sources row and the NODE_ENV row. Claude-Session: https://claude.ai/code/session_01BmsuLyUeuG5CNpZFMH1jzS Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 9 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 28 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 83c52023a36bb51eab09006505d03fde0eeaf14c && git checkout 83c52023a36bb51eab09006505d03fde0eeaf14c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin abd254508b861da8ed1e96c2a813541b3274fb40 c76e4635441bb51f7b30eff2d8a85c9a00493f15 && git checkout -B drift-repro abd254508b861da8ed1e96c2a813541b3274fb40 && git merge --no-ff c76e4635441bb51f7b30eff2d8a85c9a00493f15
node scripts/docs-audit/affected-docs.mjs --json abd254508b861da8ed1e96c2a813541b3274fb40
|
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 37886957837 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
|
Queue ejection — seat disposition (re-queued once). Session: Signature (new; no queue-flake anchor exists for this file key, and the triage comment above counts this PR as the only one to hit it in 24h):
Initial read: this is load or timing, not a regression from this PR. All three facts that allow one re-queue hold:
Pre-landing checks re-run on the unchanged head The PR is re-queued once, as is. If the same signature ejects it again, the seat stops re-queuing and hands it to a fresh diagnosis. It is not added to any flake table; this is only a flag for that ledger. Generated by Claude Code |
Fixes #22249
Clause-②: no
What changed
In the source checkout
os devserves,os doctor'sEnvironment filesrow now says whose.env*cascade it resolved:os serve/os start. The source checkout isnodeEnvSourcePosture: anobjectstack.config.tsthat boots as itself, alone or beside its owndist/objectstack.json. This carries out triage route A (comment 6057410828) under #5387's standing ruling. Doctor still resolves the cascade exactly asos servedoes, and only the row's wording in that posture changes.environmentSourcesCheck(reading, env, opts)takesopts.sourcePosture, the same option shape asnodeEnvCheck. Its default isfalse, which gives the unchanged row.run()now callsnodeEnvSourcePosture(cwd)once and hands the answer to both the sources row and theNODE_ENVrow. Before, theNODE_ENVrow computed it inline. It is still the one call site indoctor.ts, so the posture is not recomputed.doctorNodeEnv(),readDotenvFiles()and the overlay are untouched, so every env-derived verdict is the same in every posture.os startis true for anyNODE_ENV.start.tsloads.env*forNODE_ENV === 'test' ? 'test' : (NODE_ENV || 'production'), the same expressionserve.tsuses without--devand the onedoctorNodeEnv()mirrors.Measured: the rows, before and after
I ran the real
Doctorcommand class from source withtsxandNODE_ENVunset, in five fixture directories. Before is basefdfdd7e76, withdoctor.tsrestored to it in the working tree. After isc76e46354.Source posture, a freshly scaffolded project (config only, no
.env*):Source posture with comment-only
.env.productionand.env.developmentfiles. The listed file shows the cascade did not move:Controls: an artifact-only project (
dist/objectstack.json, no config), an empty directory, and a config beside anOS_ARTIFACT_PATHnaming another artifact. All three rows are byte-identical before and after (diffempty; sha256 of the three rows7c5ce42e16f56430…on both sides):Tests
New cases, all in
packages/cli/src/commands/doctor-node-env-default.test.ts(unit tier):environmentSourcesCheck, for both row shapes (no file, a.envfile):os serveandos startbesidenode_env=production;{ sourcePosture: false }equals the posture-less call field for field, and does not nameos start;status,fixand the closing provenance clause are equal across postures.Doctor.run):os serve/os startbesidenode_env=production, directly above theos devNODE_ENVrow;.env.productionand.env.developmentpresent, the row lists the production file and not the development one;environmentSourcesCheck(...)message and does not nameos start.H3 from the dispatch was measured partly false. The existing pins on this row (
doctor-env-provenance.test.ts,doctor-config-env-overlay.test.ts) assert substrings such asNo .env* files hereandnode_env=production, not the full row text. So the byte-identity control is carried by the new pins above and by the one-off measurement, not by those pins. All of them stay green.Reverse verification. With the fix committed, I restored
doctor.tsto basefdfdd7e76in the working tree only (on-disk hash14b8e959…equal to the base blob, marker count 0). Direction observed: red as expected. 4 of the 11 new cases failed, namely both unit source-posture cases plus e2e (f) and (g). All 7 controls stayed green (Tests 4 failed | 30 passed (34)). The restore leg wasgit checkout HEAD --: hash01415add…equal to the HEAD blob,git diff HEADempty, status clean. The subject resolves through source (./doctor.jsrelative import), notdist/, so no rebuild was needed for either leg.Runs, all on
c76e46354:pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 --reporter=verbose src/commands/doctor-node-env-default.test.ts:Tests 34 passed (34).doctor-node-env-default,doctor-env-provenance,doctor-config-env-overlay):Test Files 3 passed (3),Tests 68 passed (68).@objectstack/cliunit layer (vitest run --project unit --maxWorkers=2):268 passed | 2 failed (270). The 2 failures weretest/published-subpath-console.pin.test.tsandtest/published-subpath-hook-body.pin.test.ts, which threwpackages/cli is not built(a prerequisite, not a measurement). Re-run after the workspace build, both passed (Tests 29 passed (29)).pnpm --filter @objectstack/cli typecheck: exit 0 (tsc --noEmit, thencheck:test-typecheck: OK).tsc --listFiles -p tsconfig.jsoncounts the edited test file in the program (1 of 1526 files).Gates
All of these were run on
c76e46354, the final commit, after the workspace build (pnpm turbo run build --filter='!@objectstack/docs' --concurrency=2:72 successful, 72 total).node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, with no paths (change set taken from the merge basefdfdd7e76), gives 64 commands. That list is identical to the dispatch's lead list (sorteddiffempty). All 64 exit 0. Reconciled with--ran(each line carrying its recorded exit):64 derived famil(ies) accounted for — 64 run, 0 NOT-MEASURED (a DERIVED zero — all 64 recorded an exit code and none of them is 3).check:dual-build-cjs-loads,check:i18n,check:i18n-coverageandcheck:i18n-walk-parityexited 3 (PREREQUISITE NOT MET), which is not a measurement. In the second pass, after the build, all four measured green. For example,check-i18n-bundles: OK (9 package(s) — all bundles in sync, no undeclared authoring keys).check-nul-bytes: OK (scanned 10382 text file(s) … no raw ASCII control bytes);check-adr-0087-registration: this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen);No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).check-changeset-no-major --base origin/mainreports its clause-② level axis as NOT APPLICABLE because there is nopull_requestpayload. CI reads this body'sClause-②: noline.pnpm lint(eslint . --no-inline-config, whole repository, not narrowed): exit 0, no findings.pnpm --filter @objectstack/cli typecheck: exit 0.Acceptance notes
packages/cli/test/, but they live inpackages/cli/src/commands/. The new cases were added to the file the claim names,doctor-node-env-default.test.ts, where it actually is..env.developmentpresent (for exampleOS_TENANCY_POSTURE=isolatedin it), the row still opensNo .env* files here (node_env=production, the cascade …). It also saysno environment input set. That is correct underos doctor不加载.env*,读到的环境与serve/dev/start实际运行的不是同一份 —— 写在.env里的配置错误 doctor 一律看不见 #5387 for the production cascade, and the new parenthetical now says which cascade, but the opening words read literally false beside a.env.development. RewordingNo .env* files hereis outside route A's single clause. No carrier.#22163's pending changeset still says the row "still reports the.env*cascade fornode_env=production". That stays true; this PR's changeset adds the loader wording and does not edit that file.Generated by Claude Code