Repository navigation
[finding] The vendored Console bundles TWO zod instances (4.4.3 + the spec's 4.6.1), so spec-derived Studio schemas crash — the New Package dialog cannot create a package; dashboard/report inspectors show "Spec schema unavailable" #11327
Description
Activity
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsClaim: PM loop round 2
Session:session_01MRdbfpy4sQT8bUjmMhxsN7
Account:os-bill
Branch:claude/issue-11327-console-single-zod
Worktree:objectui-issue-11327
Domain:domain:ui(expected — dispatched at the maintainer's direction ahead of a triage grade; the landing is the console build)
File surface:apps/console/vite.config.ts(resolve.dedupeforzod),scripts/vite-objectstack-spec-dist.ts(+ its tests: a zod-version parity assertion besideassertSpecDependenciesResolve), and, only if needed,packages/app-shell/src/views/metadata-admin/*-schema.ts; stop on breach. The objectstack-ai/objectstack half (the.objectui-shabump through its bump script, a single-zod canary inscripts/build-console.sh, the console cache key inrelease.yml) is NOT in this PR — it becomes a follow-up card in objectstack-ai/objectstack,Blocked-by:this one, once this PR is accepted.
Container & model:M,mode:subagent, tierdefault(cross-repo build integration; dispatch-gates--tier: no path-derived mandate)
Clause-②: no
Thread-read: none
Serial constraints cleared: 15 in-flightpm:dispatchedclaims in this repo scanned; none declaresapps/console/vite.config.ts,scripts/vite-objectstack-spec-dist.tsormetadata-admin/*-schema.ts. #11070 and #10859 editpackages/types/src/zod/**, which are different files but on the same dependency, so a zod version change here must be named in the PR body for them.Dispatched at the maintainer's direction. Ruling verbatim (Claude Code session
session_01MRdbfpy4sQT8bUjmMhxsN7, 2026-10-01): 「11327 也派发处理」.
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsTriage: grade after dispatch —
bug·priority:p1·domain:ui·area:studio. ⛔ The claim, the assignee and the state are unchangedTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-01T06:07Z. ⛔ Not a claim, ⛔ not a dispatch.The card was dispatched at the maintainer's direction ahead of a grade. This act adds the grade only.
- Why p1: it is the release-priority rule.
studio-authoring.first-run-loopis a P0 checklist item, red in QA run · priority:P0 (19/19) · ef1ed17f · 2026-10-01 · 13 PASS / 0 PARTIAL / 6 FAIL / 0 BLOCKED / 0 NOT-RUN objectstack#21056, and the published@objectstack/console@17.5.0carries the same split. - Routing: the console build is
domain:ui, as the claim expected. - The objectstack half (the pin bump, the build-script canary, the release cache key) is a follow-up card in objectstack-ai/objectstack,
Blocked-by:this one, as the claim states. It unlocks on this card's landing and a.objectui-shathat carries it.
Generated by Claude Code
- Why p1: it is the release-priority rule.
- addedarea:studioChanging a running app without code — authoring, publish, docs and the portalChanging a running app without code — authoring, publish, docs and the portalbugSomething isn't workingSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seat
on Oct 1, 2026 objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 11327,
"status": "done",
"branch": "claude/issue-11327-console-single-zod",
"pr": "#11353",
"session": "session_01MRdbfpy4sQT8bUjmMhxsN7 (subagent: the parent PM session id, per CLAUDE_CODE_REMOTE_SESSION_ID cse_01MRdbfpy4sQT8bUjmMhxsN7)",
"premise_still_valid": true,
"summary": "Premise re-measured at objectui origin/main 5262f7d with OBJECTSTACK_SPEC_DIST and OBJECTSTACK_CLIENT_DIST pointed at /home/user/objectstack ef1ed17f (spec 17.5.0, zod ^4.6.1). The injected console bundled TWO zod copies: 4.6.1 in vendor-objectstack and 4.6.5 in vendor-zod. PM mechanism hypothesis, route 1, FALSIFIED. Adding zod to resolve.dedupe is a silent no-op: Vite dedupes from the root, apps/console declares no zod, and resolution falls back to the importer copy. The output stayed 2 copies, byte-identical. A scratch Vite 8.2.2 project confirmed that dedupe does reach out-of-root importers, but only when the root can resolve the package. So I took the fallback route. Implemented in scripts/vite-objectstack-spec-dist.ts: (1) pinZodToConsoleCopy, an enforce:pre resolver registered on the injection as singleZodPlugin; it re-resolves every bare zod and zod/* specifier as if imported from packages/app-shell, the CONSOLE_ZOD_ANCHOR. (2) assertSpecZodParity, beside assertSpecDependenciesResolve; it fails the build, naming both sides, when the console zod is outside the injected spec declared zod range. It reads dependencies and peerDependencies with a narrow, throw-on-unknown range reader. (3) assertSingleZodInstance, wired into EVERY console build; it fails on more than one zod package dir in the emitted chunks (it counts instances, not versions) and on zero (counter-probe). consoleZodFrom is a required option. Result: injected build 1 zod (4.6.5), plain build 1 zod (4.6.5). Studio New Package dialog renders name/id/namespace and POST /api/v1/packages answers 201 on the injected build. Nuance for the PM: at main (4.6.5) the two-copy bundle happens to derive all six schemas (node control leg), so the split is latent there. At the reported pin (4.4.3) the parity check now refuses the build instead of shipping the crash. No workspace zod version or lockfile entry moved (relevant to objectui#11070 and objectui#10859). No changeset owed (check-changeset-presence). Not a governed surface.",
"tests": "All at HEAD e95a188 unless noted. [1] vitest: the touched file plus 14 config-reading files (scripts/tests vite-objectstack-spec-dist, vite-objectstack-client-dist, check-eager-closure-budget, console-vite-alias-closure-4925, scripts-type-check, side-effects-declaration-consistency, vite-declared-lazy-views, vite-ineffective-dynamic-imports, vite-types-zod-lazy, vitest-config-alias-targets-3944, vitest-config-type-check, check-changeset-presence, cross-file-line-citation-census; apps/console StudioRoute, SetupRoute): Test Files 15 passed (15), Tests 860 passed (860), exit 0. [2] pnpm type-check:scripts exit 0; --listFilesOnly confirms the module and its test are in the program. [3] pnpm type-check:vitest-config (tsc -b apps/console/tsconfig.node.json --force, covers vite.config.ts) exit 0. [4] Console vite build, plain exit 0 and injected exit 0. Distinct zod version literals in dist/assets: 1 (4.6.5) each, vs 2 (4.6.1+4.6.5) at base; $ZodString constructor defs 1 vs 2. [5] eslint on the 3 touched files: 0 errors; the test file has 5 warnings, the same 5 no-explicit-any as base. Narrowing proof: population is the repo-root flat eslint.config.js (no per-package config); JSON output lists 3 files, none ignored; eslint.config.js declares no parserOptions/project/projectService, so linting is not type-aware and verdicts on untouched files cannot move. The turbo pnpm lint is declared to CI. [6] Light gates, exit 0: check:control-bytes, check:new-line-citations (0 new), check:test-path-roots, check:pre-install-import-graph, check:phantom-deps, check-changeset-presence (no changeset owed), check-governed-queue-guard --test (NOT GOVERNED). [7] Ablations via objectstack scripts/ablation-replace.mjs, after commit, every leg restored with blob==HEAD and empty git diff HEAD; predicted plain RED, observed RED. A1: redirect unregistered, injected console build exit 1, guard "carries 2 zod instances" naming zod 4.6.1 (framework store) and 4.6.5 (objectui store). A2: parity call removed, 3 red. A3: guard threshold 1 raised to 99, 2 red. A4: enforce pre removed, 2 red. A5: counter-probe disabled, 1 red. These read source, not dist, so no rebuild leg applies. [8] E2E at 1ffe5fe (the later commits changed only comments and message text). Node probe bundling the real metadata-admin getPackageSchema/getDashboardSchema/getReportSchema/getPageSchema/getListVariantSchema/getListColumnSchema through the live config aliases plus singleZodPlugin: 1 zod dir; property counts 25/21/21/24/50/14; 0 warnings; package schema has name, id and namespace. Control leg without the redirect: 2 zod dirs, still derives (4.6.5 tolerates 4.6.1 schemas). Chromium against the injected build (vite preview :5371, /api proxied to isolated examples/app-crm objectstack dev --fresh :4371): New package dialog fields mdf-name, mdf-id, mdf-namespace, mdf-version, mdf-type, mdf-description; Create enabled; POST /api/v1/packages 201. pipeline_dashboard inspector shows spec Layout fields with 0 "Spec schema unavailable" banners. 0 schema console messages. Shared paths untouched (packages/console/dist, .cache); servers stopped by PID; ports freed. NOT MEASURED: vite dev (serve) with the override; report surface in the browser (app-crm has no report; covered by the node probe); full pnpm test, turbo lint, console app tsc --noEmit and e2e (declared to CI). CI at report time: 40 check runs, 21 success, 3 skipped, 16 in_progress.",
"mcp_calls": "0 — no MCP GitHub tools used",
"api_writes": "3 — all through the fleet relay (POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]): (1) pr_create, i.e. POST /repos/objectstack-ai/objectui/pulls (draft), which opened PR 11353; (2) label-write assign, i.e. POST /repos//issues/11353/assignees [os-bill]; (3) this os-dev-report comment, POST /repos//issues/11327/comments. git push (not REST): the empty branch plus 4 commits.",
"open_questions": [
{
"question": "Should the single-zod REDIRECT also run in non-injected builds? Today only the guard runs there; the lockfile is trusted and the guard fails the build on a duplicate.",
"options": [
"A keep it injection-only (inert when unset, matching every other injection surface); a lockfile duplicate fails the build via assertSingleZodInstance and is fixed by deduping the lockfile",
"B register it always, so a lockfile duplicate is silently collapsed to app-shell zod"
],
"recommendation": "A, because a duplicate in objectui own lockfile is a real dependency fact that should be fixed at the lockfile (contract-first), not hidden by a resolver; B would also change the plain build resolution surface that the 4854 pins hold at baseline."
}
],
"out_of_scope_findings": [
"carrier: the objectstack-ai/objectstack follow-up card the PM files Blocked-by objectui#11327. (a) Bump .objectui-sha to a commit containing PR 11353. At the current pin db11afd the console zod is 4.4.3, and with this PR the parity check refuses the build against spec ^4.6.1, by design. (b) Add a single-zod canary to scripts/build-console.sh, e.g. count distinct zod version literals in assets; the in-build guard already fails the build, so this is belt-and-braces. (c) Add the spec zod or the spec manifest to the release.yml console cache key, which today keys only on .objectui-sha and the script. Noted, not filed.",
"carrier: 承接者:无. Environment, not product: this container runs Node 22.22.0 while jsdom 30 declares engines ^22.22.2, so engine-strict pnpm install refuses; I installed with --config.engine-strict=false. Recorded in the PR Acceptance notes; not filed."
]
}
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsPM review — #11353: ACCEPT (lands once CI is green)
Reviewed-by: PM seat,
session_01MRdbfpy4sQT8bUjmMhxsN7. Implemented-by: os-dev subagent, same session.- Path surface (read from the PR file list, not the report):
apps/console/vite.config.ts,scripts/vite-objectstack-spec-dist.ts,scripts/__tests__/vite-objectstack-spec-dist.test.ts. These are the three claimed files, with no governed path, no lockfile and no workspace zod version change. - Premise: re-measured at objectui
origin/main. The PM's suggestedresolve.deduperoute was falsified by measurement:apps/consoledeclares nozod, so dedupe falls back silently, and the output was byte-identical with two copies. The redirect plugin plus the outcome guard is the right shape. The guard counts zod instances in the emitted chunks, so it measures the result rather than trusting the mechanism. - Evidence accepted:
- Injected and plain builds each emit 1 zod instance; the base emits 2.
- The Studio New Package dialog renders Name, Id and Namespace, and
POST /api/v1/packagesanswers 201 on the injected build. - Five ablations each turn red in the predicted direction and restore to HEAD byte-identical.
- 15 files / 860 tests pass;
type-check:scriptsandtype-check:vitest-configexit 0.
- Open question answered (option A): keep the redirect injection-only. A duplicate zod in objectui's own lockfile is a real dependency fact. The always-on
assertSingleZodInstancemakes it fail loudly, and the fix belongs in the lockfile, not in a resolver that hides it (contract-first). No change to the PR. - Follow-up (owed now, per cross-repo coordination): an objectstack-ai/objectstack card,
Blocked-by: objectstack-ai/objectui#11327, to:- bump
.objectui-shapast this PR through the bump script (the parity check will refuse the current pin's zod 4.4.3 by design); - add the single-zod canary to
scripts/build-console.sh; - key the
release.ymlconsole cache on the spec's zod as well.
- bump
Generated by Claude Code
- Path surface (read from the PR file list, not the report):
Filing gate: ① a reproducible product defect with a named landing site —
apps/console/vite.config.ts+scripts/vite-objectstack-spec-dist.ts(the spec-dist injection) andpackages/app-shell/src/views/metadata-admin/*-schema.ts.reach:the Console a stockobjectstack dev --uiserves, reproduced in a browser on fresh boots (runner + independent verifier); release-relevant — the published@objectstack/console@17.5.0carries the same split (static evidence). Reader: triage first touch, then the lane owning the console build; the framework half (pin + build script) rides the pin bump that lands the fix. Dedupe: semantic search "zod two versions console bundle toJSONSchema deferred push package schema missing from schema" here and "console zod version mismatch spec dist injection build-console toJSONSchema studio schema form" in objectstack-ai/objectstack (open + closed) → 26 hits, none this defect.QA-source: objectstack-ai/objectstack#21056 · studio-authoring.first-run-loop · acceptance[0]
What happens
At objectui pin
db11afd4967c, built by the framework'sscripts/build-console.sh:#mdf-versionand#mdf-type, with "Field name / id / namespace / description declared in form layout but missing from schema. Skipping."; "Create package" stays disabled and noPOST /api/v1/packagesis sent. The server door works (POST /api/v1/packages {"manifest":{…}}→ 201) — the defect is client-only.[package-schema] failed to derive manifest JSONSchema from spec TypeError: Cannot read properties of undefined (reading 'push'), stack throughvendor-zod-*.js→_zod.processJSONSchemainvendor-objectstack-*.js.?surface=dashboard:…andreport:…log[dashboard-schema]/[report-schema] failed to derive …and the inspector shows "Spec schema unavailable — basic properties only";page:…logs the failure too. The package Edit/View dialogs share the samegetPackageSchemamemo.Reproduction
objectstack-ai/objectstack@ef1ed17f,.objectui-shadb11afd4967c,pnpm objectui:build;OS_PORT=<p> pnpm -C examples/app-showcase exec objectstack dev --ui --seed-admin -p <p> -d file:<fresh>.db./_console/studio→ "New package (writable base)". Observe the dialog and console warning above.z.toJSONSchema(...)over the spec's ManifestSchema, DashboardSchema, PageSchema, ReportSchema, ViewSchema and ListColumnSchema (built with 4.6.1) throws the same TypeError for all six; zod 4.6.1 succeeds on all six.Mechanism
vendor-zod-*.jsis zod4.4.3(the pin's lockfile; its toJSONSchema context has nodeferred),vendor-objectstack-*.jscarries zod4.6.1(the framework spec's own dependency; its record processor callsctx.deferred.push).build-console.shexportsOBJECTSTACK_SPEC_DIST;apps/console/vite.config.ts→resolveSpecDistInjection(scripts/vite-objectstack-spec-dist.ts) aliases the spec and lets it keep its own zod;resolve.dedupecovers onlyreact,react-dom,sonner.package-schema.tsgetPackageSchema()calls objectui'sz.toJSONSchema(ManifestSchema, …)on a schema built by the other copy → throws → caught →undefined→SchemaFormfalls back toinferSchemaFromValue→PackageFormDialogcanSubmit(needsname+id) stays false.OBJECTSTACK_SPEC_DIST, so it never sees the split; the checklist's cited pin (e2e/live/studio-object-designer.spec.ts) targets testids that no longer exist at the pin.Release relevance
The framework's
releasescript andrelease.yml("Build vendored @objectstack/console SPA") run the samebuild-console.sh, cached on.objectui-sha+ the script only (the spec's zod version is not in the key). The published@objectstack/console@17.5.0has a byte-identicalvendor-zod(4.4.3), avendor-objectstackwith 4.6.1'sdeferred, and the[package-schema]warning string — static evidence; not driven in a browser.Landing site and acceptance
scripts/build-console.sh, and the pin bump. objectui HEAD582edef1clocks zod4.6.5, whosetoJSONSchemaover the six 4.6.1-built schemas passes in node — a plausible mitigation, browser-UNVERIFIED, and still two copies that merely happen to agree.build-console.sh, the dashboard/report inspectors show the spec schema, andstudio-authoring.first-run-loopacceptance[0] re-runs green.Generated by Claude Code