Repository navigation
fix(cli): a define* helper's ZodError at config load is reported as one located refusal, not a raw JSON array - #29087
Conversation
A define* helper's Schema.parse throws its ZodError as is, and every command that loads the config printed error.message: the issues as a JSON array, with no file and no helper. The config module now loads through bundleConfigModule, which reads the evaluated bundle's inline source map to locate the authored call and renders the issues with formatZodError. os serve's own config load goes through the same seam. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FEkQ5mch1VryTyQsMG5U86
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FEkQ5mch1VryTyQsMG5U86
📓 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:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 35 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 5508d6a8efe708815cedc1a2da0e2c192d761e1f && git checkout 5508d6a8efe708815cedc1a2da0e2c192d761e1f
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d1f3ac05038cffc28b9f8bcbe2116afe1b5f1a2e 5af532cccc59562cd116e709f2af0d7276085165 && git checkout -B drift-repro d1f3ac05038cffc28b9f8bcbe2116afe1b5f1a2e && git merge --no-ff 5af532cccc59562cd116e709f2af0d7276085165
node scripts/docs-audit/affected-docs.mjs --json d1f3ac05038cffc28b9f8bcbe2116afe1b5f1a2e
|
|
domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo 席位核对ACCEPT · The maintainer's standing authorisation for landing reviewed PRs, verbatim: 「按照 pm-dispatch 规则复核过的都授权」.
Generated by Claude Code |
|
domain:server#2 · os-steve · session_01ADHkmQxwoGb5fSnrFkpxmt Landing record · PR #29087
Generated by Claude Code |
Requested by Leehom · project thread
Fixes #28956
Clause-②: no — CLI output text only. No schema, export or accepted input changes; a refused config is refused with the same exit code as before.
Metadata-protocol: none
维护者速读
defineView等define*helper 抛出的 ZodError,现在打印成一条带定位的拒绝(文件:行:列 · helper · 路径 · zod 原话),不再是 40 多行 JSON 数组。os validate、os build、os lint、os dev、os serve一致。npm run validate只给 JSON,不说哪个文件、哪个视图,人和 coding agent 都没法自我纠正。defineStack的拒绝原样不动。回滚 = revert 本 PR。packages/cli/src/utils/config-load-refusal.ts(定位逻辑,locateConfigLoadZodError只在栈和 source map 都对得上时才给文件);packages/cli/src/utils/config.ts的bundleConfigModule;packages/cli/src/commands/serve.ts一行改动。pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2 test/config-load-zod-refusal.test.ts,预期 9/9 通过,用例里能看到src/views/ticket.view.ts:5:28 · defineView validation failed (1 issue):。What changes
defineView,defineActionand the other strict helpers callSchema.parseand throw theZodErroras is. On the normal load path nothing wrapped it, and each command's catch-all printederror.message, which for aZodErroris its issues as a JSON array.bundleConfigModuleinpackages/cli/src/utils/config.tsis now the one place the config module graph is bundled and evaluated.loadConfig(validate, build, lint, dev's compile step, info, diff, generate, doctor, verify, i18n,migrate meta) andos serve's own load both go through it.os servehad its ownbundleRequirecall and printed the same raw array, so it is routed through the seam too (one line inserve.ts).ZodErrorthrown while the module evaluates is rethrown as a plainConfigLoadRefusalErrorwhose message isFILE:LINE:COL · HELPER validation failed (N issues):followed by the blockformatZodErroralready renders (the same formatter theos migrate metashim's__refusaland thedefineStack validation failedblock use). There is no second formatter. TheZodErrorrides ascause. Because it is a plainError, no catch-all changed: the text face prints it where it printed the array, and--jsoncopies it intoerror. Nocodeis added.define*function and the authored line spells that name. With--enable-source-mapsthe frame is already authored and is matched against the map's sources.defineView: the container defines no views …),defineStack's formatted refusal.Card output, before and after (
os validateon the card's project, adefineViewwithcolumns: [{ field: 'subject', link: true }, 'status', 'priority']insrc/views/ticket.view.ts, re-exported throughsrc/views/index.ts):Tests
New
packages/cli/test/config-load-zod-refusal.test.ts(unit tier, in-process, src imports):os validate --json: exit 1,errornamessrc/views/ticket.view.ts:5:28,defineView,list.columnsand the branch issue; it does not start with[.os validatetext face: no✗ [line, no"code": "invalid_union".os build --json: the same located head, and the sameerrorasos validate; exit code unchanged.define*helper (ObjectSchema.createwith a badname) is located (objectstack.config.ts:3:N · Validation failed (1 issue):) without naming a helper.Acceptance notes
union-branch-policy.ts) picks thestring[]branch, so the line readslist.columns.0: Invalid input: expected string, received object. It is located and correctable, but it is not the card's suggested "mix of strings and column objects; use one form". That wording would need a union message inpackages/spec(a spec change with contract review), which the order told this card not to make.define*only.ObjectSchema.create,Field.*and other non-define*calls get the file, line and column, and the defaultValidation failedheader.os create'sutils/scaffold-validate.tshas its ownbundleRequireover the template it just generated, which is not authored input.config.tsand thevalidate.tscatch-all.validate.tsis not touched. Added:utils/config-load-refusal.ts(new) and one line incommands/serve.ts, becauseos serveloads the config withoutloadConfigand printed the same array.packages/cli/src/utils/config.ts,commands/serve.ts: none. fix(cli)!: os validate and os build refuse a flow whose region the automation engine refuses to register #29043 touchescommands/compile.tsandcommands/validate.ts(not touched here); fix(cli): os migrate meta --json says when no migration step ran #29045 touchesmigrate/meta.ts(not touched here).Verification
Real CLI, red on main 0be247c and green on this branch (validate, build, lint, dev, serve)
Red, built CLI from
origin/main0be247c, card project:os validate: exit 1,✗ [then the array (45 lines of output).os build: exit 2, same array.os lint: exit 1, same array.os dev -p 38956: exit 1, the compile step prints the same array.os serve -p 38958: exit 1,Loading objectstack.config.ts...then the same array.Green, this branch:
os validate: exit 1,✗ src/views/ticket.view.ts:5:28 · defineView validation failed (1 issue):and the two issue lines.os build: exit 2 (unchanged), same block.os lint: exit 1, same block.os dev: exit 1, same block then✗ Compile failed — fix errors above before starting dev server.os serve: exit 1, same block.os validate --json:"error": "src/views/ticket.view.ts:5:28 · defineView validation failed (1 issue):\n\n ✗ list.columns: Invalid input\n ✗ list.columns.0: Invalid input: expected string, received object".NODE_OPTIONS=--enable-source-maps os validate: same located head.src/withos validate ../objectstack.config.ts:views/ticket.view.ts:5:28 · …(relative to the working directory).Controls on this branch: a syntax-error config prints esbuild's
Build failed with 1 error: … Expected identifier but found end of file; a missing import printsCould not resolve "./src/not-there"; a clean config validates (✓ Validation passed); a badmanifest.idstill prints✗ defineStack validation failed (1 issue):;os migrate meta --from 17on the card project still prints the shim's[authored-source] defineView(): the current schema refuses this artifact … defineView validation failed (1 issue):warning and exits 0.Ablation (src-level; the test imports src, no dist hop)
node scripts/ablation-replace.mjs --file packages/cli/src/utils/config.ts --anchor ' if (isRawZodError(error)) throw configLoadRefusal(error, absolutePath, bundle, cwd);' --delete -- pnpm exec vitest run test/config-load-zod-refusal.test.ts:ok mutation landed: anchor 1 -> 0, blob 82be1e8df47c -> f63bad0a3d26Tests 4 failed | 5 passed (9): the four seam cases (validate--json, validate text face, build--json, the non-define*locate). The renderer's unit case and the four controls stay green.ok restored: blob == HEAD (82be1e8df47c) and git diff HEAD is emptyLocal gates
Head
5af532cc.vitest run --project unit): Test Files 369 passed (369), Tests 5205 passed (5205). The integration tier is declared to CI (no spawn entry or integration file touched).typecheck(tsc --noEmit+check:test-typecheck): exit 0.node scripts/pm/dispatch-gates.mjs --commands: 63 derived families, every one run with its exit code captured before any pipe: 63 × exit 0.--ran:63 derived famil(ies) accounted for — 63 run, 0 NOT-MEASURED (a DERIVED zero).check:i18n-coveragefirst answered exit 3 (PREREQUISITE:@objectstack/connector-slack/service-printhad no build output); after building the./examples/*^...closure it answered exit 0 (OK (14 config(s), 426 baselined untranslated string(s), none new)).Generated by Claude Code