… navigation item type
objectstack#19789 widened the spec's navigation item union with `doc`, and
`NAV_TYPE_META` is a Record keyed by that spec-derived union, so objectui
stopped compiling against @objectstack/spec built from objectstack main (the
Spec Main Shape Gate, TS2741 on NavigationDesigner).
- `doc` entry: `appDesigner.navTypeDoc`, `bg-blue-100 text-blue-700`, `BookOpen`.
- The map is typed `Record<NavigationItemType | 'doc', ...>`: the pinned spec
17.4.0 has no `doc`, so a plain `doc:` key is an excess property there. The
`| 'doc'` goes at the pin bump that ships `doc`.
- `appDesigner.navTypeDoc` fallback in useDesignerTranslation and all ten
locale packs.
- Guard test: every discriminant of the installed spec's NavigationItemSchema
renders a row with a resolved type badge (spec subset of map, not equality).
- `doc` is deliberately NOT in QUICK_ADD_TYPES: an empty doc item fails the
spec's book-or-doc requirement; authoring it is objectui#10188.
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BA3nKVUwKQJf8DBxrSVtNC
Fixes #10287
Clause-②: no
What was wrong
objectstack#19789 added a
docmember to the spec's navigation item union. Inpackages/plugin-designer/src/NavigationDesigner.tsx,NAV_TYPE_METAis aRecordkeyed by the spec-derivedNavigationItemType, and it had nine entries and nodoc. SoSpec Main Shape Gate, which compiles objectui against@objectstack/specbuilt from objectstackmain, failed with TS2741 on that map onmainand on every PR head. The TS7016 errors in app-shell'sAppContent.tsxwere a knock-on effect of the failedplugin-designerbuild.What changed
NAV_TYPE_METAgets adocentry:labelKey: 'appDesigner.navTypeDoc',bg-blue-100 text-blue-700(a colour none of the other nine use), and lucideBookOpen.@objectstack/spec17.4.0 has nodoc, so a plaindoc:key is an excess property there (TS2353). objectstackmainhasdoc, so the gate fails without the key (TS2741). The map is now aRecordkeyed byNavigationItemType | 'doc', which compiles against both. A comment in the code says the| 'doc'goes at the pin bump that shipsdoc. The map is still aRecordover the spec-derived union, notPartialand not string-keyed.appDesigner.navTypeDocis added to theuseDesignerTranslationEnglish fallback table and to all ten locale packs, each in its own language.packages/plugin-designer/src/__tests__/NavigationDesigner.specNavTypes.test.tsx: it reads the discriminants of the installed spec'sNavigationItemSchemaat runtime, renders one item of each type in the tree and the live preview, and asserts that the type badge shows a label and not a raw key. It checks that every spec type is in the map, not that the two sets are equal, because the map hasdocbefore the pin does. Run against the pinned spec it covers 9 types. Run against a spec built frommainit covers 10.docis not added toQUICK_ADD_TYPES. An emptydocitem fails the spec's book-or-doc requirement, and authoring one belongs to objectui#10188.@object-ui/plugin-designerand@object-ui/i18npatch.Verification (every run below is on head
6f970da0)The gate's own path, reproduced locally. objectstack
mainwas6bf7f1c0b441when this was read. I made a sparse, blobless clone of it the way the workflow does, ranpnpm --filter @objectstack/spec build, rannpm pack --ignore-scripts, and rannode scripts/spec-main-shape-gate.mjs injectinto a separate objectui worktree. The inject step re-pointed zod to 4.6.1, which is the gate's own behaviour. Then I ran a forced turbotype-check --continuein that worktree, with a private cache dir.8b1f0661(origin/main)--filter=@object-ui/plugin-designer(17 tasks)plugin-designer:type-checkfails atNavigationDesigner.tsx(82,7)with TS2741 "Property 'doc' is missing". The gate'sreportstep exits 1 and attributes the error to that file and to6bf7f1c0b441.6f970da0(this branch)--filter=...@object-ui/plugin-designer(40 tasks)Tasks: 40 successful, 40 total, 0error TS. The report step exits 0: "objectui type-checks against@objectstack/specat that commit."The GREEN scope is plugin-designer and its dependents:
plugin-designer,app-shell,console,example-console-starterandexample-byo-backend-consoletype-check, plus 33 builds, including theplugin-designerandapp-shellbuilds that the failure had been hiding. Nothing new surfaces once the build is unblocked. I narrowed the scope on purpose: a package that does not depend on plugin-designer cannot be moved by this diff, and the full-graph run is this PR's ownSpec Main Shape Gatecheck.Pinned spec (17.4.0), normal path.
pnpm --filter @object-ui/plugin-designer --filter @object-ui/i18n type-checkpasses. Bothtsc --noEmit && tsc -p tsconfig.test.jsonscripts echoed.tsc -p tsconfig.test.json --listFilesincludes the new test file.Ablation. I committed the change first.
ablation-replace.mjsdeleted thedocentry line and verified it on disk (anchor count 1 to 0, blobdc6cc9b5f055toa1ec0cb78efb). It then restored the file to HEAD and proved the restore (the blob matches HEAD's andgit diff HEADis empty).docentrydocentry deleteddoctype…",TypeError: Cannot read properties of undefined (reading 'Icon')Recordkey now includes'doc'| 'doc'removed from the keySuites, run from the repo root:
pnpm exec vitest run packages/plugin-designer/ packages/i18n/ packages/app-shell/src/__tests__/defaults-maps-mirror-en-pack.test.tsx:Test Files 94 passed (94),Tests 1340 passed (1340).all-locales-key-parity,defaults-maps-mirror-en-pack, the guard test):3 passed,57 tests passed.Gates, all exit 0:
check:i18n-keys,check:i18n-drift(1 key added, 0 en values changed),check:i18n-dead-keys(report only;navTypeDoclands in needs-review like its nine siblings, because the key is reached through the map and not through a literalt()call),check:control-bytes,check:new-line-citations(0 new);node scripts/check-changeset-presence.mjs,check:changeset-claims,check:pending-changeset-literals;check:phantom-deps,check:unused-deps,check:icon-record-names,check:spec-symbols,check:test-path-roots,check:designer-field-key-parity,check:installed-pin-claims,check:published-tsconfig-exclude,check:published-dist,check:vi-mock-inherit,check:unreferenced-sources.Lint, narrowed to the files this PR touches:
pnpm lintisturbo run lint, which runseslint .per package.eslint --print-configresolves a config for each of the 13 changed.ts/.tsxfiles, so none of them is ignored.eslint --no-inline-config --format jsonon those 13 files reports 0 errors and 4 warnings. All 4 are inNavigationDesigner.tsx. The base version of that file, linted through stdin, gives the same 4 warnings, and none of them is on a changed line.eslint.config.jsdoes not turn on type-aware linting (noparserOptions.projectorprojectService), and no rule undereslint-rules/reads other files. So this diff cannot change the verdict on any file it does not touch.NOT MEASURED:
check:eager-locale-catalogues: PREREQUISITE NOT MET, it needsapps/console/dist/eager-closure.json.check:doc-examples: prerequisite not met, it needs builtdist/for about 20 packages. This diff touches no doc example.Acceptance notes
docbelong to objectui#10188, not this PR. They are theNavigationRendererandnavSurfacedefault:fallthroughs, theAppNavCanvasKIND_TONEfallback,AppCreationWizardTYPE_BADGE_COLORS, the hand-written nine-memberNavigationItemTypeSchemainpackages/types, and quick-add or authoring ofdocitems.NavigationItemTypeinpackages/types/src/app.tsstill describesNAV_TYPE_METAas keyed by exactlyNavigationItemType. The point it makes (a new spec member breaks the compile loudly) still holds. The wording goes back to exact at the pin bump. It is outside this surface, so I left it.node_modules. So a gate reproduction in a linked worktree writes build outputs compiled against the injected spec into the cache that sibling worktrees read. The workflow'sTURBO_FORCEbypasses cache reads but does not stop the writes. Pass--cache-dirpointing at a private directory. My first attempt did write 6 entries before I stopped it. I compared each of them with the same package built against the pinned spec, and all 6 are byte-identical, so nothing wrong is in the shared cache.Written by the
domain:uiseat #1 dev agent, sessionhttps://claude.ai/code/session_01BA3nKVUwKQJf8DBxrSVtNC.Generated by Claude Code