Repository navigation
fix(types): a nav item's visible takes the spec's expression input (envelope accepted, blank string refused) - #13086
Merged
objectstack-fleet[bot] merged 4 commits intoOct 19, 2026
Conversation
…onInputSchema by reference
The navigation mirror typed `visible` as `boolean | string`, so it refused
the `{ dialect, source }` envelope the spec's own parse produces and
accepted a blank string the spec refuses. The slot now hands every
non-boolean value to `@objectstack/spec`'s `EvaluatedExpressionInputSchema`
(without its string transform), keeps the pinned boolean arm, and the TS
type takes the spec's `EvaluatedExpressionInput` by reference. Parity is
pinned both ways in navigation-spec-parity.test.ts.
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB
Contributor
ℹ️ Console Performance Budget — not measuredThis run did not produce a console bundle to measure, so there is no pass/fail verdict for the performance budget. This is not a budget violation. Nothing was measured — the numbers a real violation would carry are simply absent.
See the workflow run for details. No package size report: it is only generated from a complete package build, so a partial one is never shown. |
…'visible']
Type-only follow-through of the widened NavigationItem['visible']
(boolean | EvaluatedExpressionInput): layout's exported
VisibilityEvaluator and the app-shell nav gates' evalVis callbacks take
the item's own visible type, so the spec's { dialect, source } envelope
type-checks on its way to evaluateNavigationVisibility, which already
evaluates it. No runtime change.
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
…isible'] The test's evaluator callbacks were typed string | boolean | undefined and no longer satisfy the widened VisibilityEvaluator. Type-only. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
objectstack-fleet
Bot
deleted the
claude/issue-13075-nav-visible-envelope
branch
October 19, 2026 21:30
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #13075
Clause-②: yes
What changes
@object-ui/types' navigation mirror typed an entry'svisibleasboolean | string. The spec types every navigation branch'svisibleasEvaluatedExpressionInputSchema, so the two disagreed both ways: the mirror refused the{ dialect, source }envelope the spec accepts, and accepted a blank string the spec refuses.packages/types/src/zod/app.zod.ts:NavigationItemObject.visibleis nowNavigationVisibleSchema. Its accept set isboolean, a string, or the spec's ownEvaluatedExpressionSchema(by reference). Every non-boolean value is handed to the spec'sEvaluatedExpressionInputSchema(imported from@objectstack/spec/shared, not restated), and that schema's issues are the refusal, with the spec's code, path and sentence. The spec's string transform is not taken: this mirror writes nothing into an author's document (decision batches Redesign examples based on new JSON project specification #69 / [WIP] Fix action run issue in CI/CD pipeline #90), so a bare string stays a string. This is the same spellingvisibleWhen's wire uses inbase.zod.ts. The judge runs only on a value an arm already took, so one fault is reported once. Thedocarm reads the same slot, so it changes the same way.packages/types/src/app.ts:NavigationItem['visible']isboolean | EvaluatedExpressionInput, the spec's input type by reference.navigation-spec-parity.test.ts: a new block pins parity both ways. The envelope is accepted by both, a blank or whitespace string is refused by both with the same code and sentence, and a blanksourceinside the envelope is refused by both once atvisible.source. Thevisible: booleanrow stays as the one recorded divergence, with its existing reason.6276914186(A). Three callback parameters change fromstring | boolean | undefinedtoNavigationItem['visible']: layout's exportedVisibilityEvaluator(NavigationRenderer.tsx), and theevalViscallbacks in app-shell'shooks/useNavMemberGates.tsandviews/nav-menu-renderer.tsx. A fourth site,packages/app-shell/src/layout/__tests__/navItemVisibleEvaluated.test.tsx, has two callback annotations widened the same way. app-shell'stsconfig.test.jsonfound it (3x TS2322 understrictFunctionTypes). There is no runtime change: every reader already passes the value todefaultVisibility/evaluateNavigationVisibility, which handle the envelope..changeset/13075-nav-visible-envelope.md:@object-ui/typesminor(widening by envelope, breaking narrowing on blank strings).@object-ui/layoutminor, becauseVisibilityEvaluatoris on the package entry throughexport * from './NavigationRenderer', and widening a callback parameter breaks a host evaluator annotated with the old narrower type.@object-ui/app-shellpatch, because nothing widened there is on its entry.Reach (measured from source)
defineAppin@objectstack/spec(packages/spec/src/ui/app.zod.ts) returnsAppSchema.parse(config). The spec's parse rewrites avisiblestring into{ dialect: 'cel', source }, so any app authored through it carries the envelope from then on. The spec also documents the envelope as the slot's normal form. Not measured: an end-to-end read of a served app's navigation through the console.visible.evaluateNavigationVisibility(@object-ui/app-shell,ExpressionProvider.tsx) takesstring | boolean | { dialect, source }and hands it toExpressionEvaluator.evaluateCondition, which routes a CEL envelope to the CEL engine.providers/__tests__/navigationVisibility-12700.test.tspins it with envelopes. There is no renderer gap.Pins and ablation
NavigationItemSchema, for a url entry and a doc entry, and is kept as written.''and' 'are refused by both schemas:invalid_union @ visible, with the same sentence.trueandfalsestill parse on the mirror and are refused by the spec.data. The card's own example (data.total == 1) is now refused by the spec's data-source rule. That rule is objectui#13076's chain, and that card'ssuperRefinehook is left alone here.Ablation at
46731c3293:scripts/ablation-replace.mjs(objectstack) put backvisible: z.union([z.boolean(), z.string()]).optional()(anchor 1 to 0, blob9e64d7e86420tocc23dad3b72b).navigation-spec-parity.test.tsthen went6 failed | 45 passed. The failures were the two envelope rows, the two blank-string rows on the url entry, the blank-sourcerow and the typed-envelope row. The controls stayed green. The restore was proven by blob hash: after the restore the blob was9e64d7e86420, equal to the HEAD blob, andgit diff HEADwas empty.Gates
At
bec1a8a688(after the ruling's widenings):pnpm --filter '@object-ui/app-shell^...' build(covers types and layout) exited 0.pnpm --filter @object-ui/layout type-checkexited 0.pnpm --filter @object-ui/app-shell type-checkexited 0. Its first run was red: 3x TS2322 innavItemVisibleEvaluated.test.tsx, fixed as above.useNavMemberGates,nav-menu-rendererorNavigationRenderer, plusnavigationVisibility-12700. layout: 22 files / 260 tests passed. app-shell: 26 files / 158 tests passed.check:spec-floorsexited 1 withno-artifact, as before..ts/.tsxfiles: 0 errors and 37 warnings, all existing and none on touched lines.At
46731c3293(the types half, unchanged since):pnpm --filter @object-ui/types type-check: exit 0.tsconfig.test.jsonlistsnavigation-spec-parity.test.ts(checked with--listFilesOnly).navigation-spec-parity+spec-derived-unionsgave 64/64 passed. The 27 test files inpackages/typesthat nameapp.zod,../app.jsorNavigationItemSchema, plusimported-defaults-8317, gave887 passed (887). The fullpackages/types/directory is declared to CI and was not run locally (per the dispatch's lock clause).eslint --format jsonover the three changed.tsfiles: 3 files, 0 errors, 1 warning. The warning is the existingz.ZodTypeany onNavigationItemSchema, not new.eslint.config.jsdoes not enable type-aware linting, so this diff cannot change the verdict on any untouched file.check:phantom-deps,self-import,unreferenced-sources,doc-example-readers,handler-key-reads,side-effects-array,element-data-source-declaration,spec-symbols,action-forward-parity,designer-field-key-parity,icon-record-names,i18n-keys,i18n-drift,i18n-designer-parity,control-bytes,check-hand-rolled-comment-mask.mjs,check-changeset-fixed.mjs,check-changeset-presence.mjs,check:new-line-citations.check:spec-floorsexited 1 withno-artifact(the workspace is not built); per the dispatch this is recorded and not fixed.Acceptance notes
visibleWhenprecedent (base.zod.ts,VisibleWhenInputSchema), not a literalz.union([z.boolean(), EvaluatedExpressionInputSchema]). The literal union would rewrite every stringvisibleinto an envelope at parse, against decision batches Redesign examples based on new JSON project specification #69 / [WIP] Fix action run issue in CI/CD pipeline #90. It would also turn red the existingdocrow that asserts the mirror keeps a stringvisibleas written.The session for this change is
https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB.Generated by Claude Code