Repository navigation
fix(app-shell): Studio's nav save leaves out entries with no target, and its banner clears on the next landed save (objectui#11776) - #11835
Merged
objectstack-fleet[bot] merged 5 commits intoOct 7, 2026
Conversation
…and its banner clears on the next landed save (objectui#11776)
"Add nav item" births { id, type: 'object' }, which the spec refuses until an
object is picked; the nav save sent it, so every autosave failed with
"navigation.N.objectName - Invalid input". The save now sends the editor's
navigation less every entry that names no target for its type, at every
depth (navPayloadOf, beside AppNavCanvas's own target reader), and keeps the
buffer dirty while it holds such an entry, so the entry stays on the canvas
in its place until it is bound.
The nav save's failure is held apart from the pillar's error and cleared when
a nav save lands. The row's remove control is in the tab order and shown on
keyboard focus.
Claude-Session: https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z
Co-authored-by: Claude <noreply@anthropic.com>
…h a save sends (objectui#11776) objectui#11189, #11204, #11232 and #11167 took "Add nav item" alone as their edit. That entry names no target, which the spec refuses; the double took it because it parses nothing. A save now leaves such an entry out, so the edit these pins send is an Add bound to a fixture object in its inspector. The two "sends nothing" pins that a bare Add would now pass vacuously measure a sendable edit again. What each pin pins is unchanged. Claude-Session: https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z Co-authored-by: Claude <noreply@anthropic.com>
… so it reads the banner alone (objectui#11776) Claude-Session: https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z Co-authored-by: Claude <noreply@anthropic.com>
… card lays out as before (objectui#11776) Always mounted at full width, the invisible control cost every card's label 32px at rest; measured in Chromium, "Landing menu" truncated. Hidden, it now has no width and gives back the row's gap; the card's hover, the card's keyboard focus and its own keyboard focus show it at full size. Claude-Session: https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z Co-authored-by: Claude <noreply@anthropic.com>
…s module (objectui#11776) It reads the card's own target table, so it lives beside it; it is a pure function, not a component, and the package entry does not re-export it. The inline disable follows the file-level precedent renderStudioGridList set, so the touched files lint with BASE's warning set. Claude-Session: https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z Co-authored-by: Claude <noreply@anthropic.com>
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-11776-nav-placeholder-save
branch
October 7, 2026 22:48
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 #11776
Clause-②: no
Studio's navigation editor no longer sends an unbound "Add nav item" entry, so autosave stops failing while one is on the canvas. The error banner from a failed nav save now clears when a later nav save lands. Each row's remove control can now be reached and used from the keyboard.
Left out of this PR: the Changes panel's no-op "Update" row (the card's fourth expectation). The seat cut it to objectui#11807, and nothing here touches the Changes panel.
What changed
navPayloadOf(inAppNavCanvas.tsx) returns the editor's navigation with every entry that names no target for itstypeleft out, at every depth.groupis always kept. An entry with notypeis still left out, as before.typeis read from one table,NAV_TARGET_READS. The card's ownnavTargetreads the same table, so what a card shows as its target and what the save treats as a target cannot drift apart.doNavSavecalls it. The spec and the server are unchanged.navError, shown in the same canvas banner.error) is left alone. A pin covers this.The PM's hypotheses, measured
{ id, type: 'object' }, and the old filter indoNavSavedropped only entries with notype.group, and inside anobject's nested views).InterfacesPillar's pillar-localerrorstate, set bydoNavSave's catch.useDraftAutoSaveholds no error state and is untouched, so no other pillar is involved.spanwithrole="button",tabIndex={0}, and an accessible name ("Remove nav item"). So noengine.*row is added.hoverstate was true (onMouseEnter/onMouseLeave), so for a keyboard user it was not in the DOM at all.idran after the filter and used the filtered index, so leaving an entry out moved a generated id. A spec-validseparatormay have no id, so this was reachable.Real-browser reading (dev-only harness, not committed)
Chromium, 1440×900. The harness mounted
InterfacesPillarthe way the surface mounts it, over the real console metadata client. A Playwright route handler played the server: it parsed every app PUT with the spec'sAppSchemaand answered refusals in the/metawrite door's issue shape. The harness was then removed (git statusclean).8273f4bnav_home, nav_landing, nav_item_3(type object, no objectName). Banner: "• navigation.2.objectName — Invalid input: expected string, received undefined"nav_home, nav_landing. No banner.nav_item_3still on the canvas, third. Leave guard holds…, nav_item_4, then PUT 200…, nav_item_3, nav_item_4. Leave guard released:focus-visible. Tab from the card lands on itThe label-width row matters. The first version of this branch always reserved the control's 24px, which cost every label 32px at rest ("Landing menu" truncated at 53 of 79px). Commit
194fa98fixes that.Pins
AppNavCanvas.navPayload-11776.test.tsx:NavigationItemSchemadiscriminator, read from the schema itself. It holds that the save leaves an entry out exactly when the spec refuses the bare entry, and keeps it once its target is set.doc(bydocorbook),action(byactionDef.actionName), no-type, the empty-string boundary (the spec accepts any string), an undeclaredtype(sent, for the server to refuse by name), nesting at every depth, order and references, and the all-bound control.StudioDesignSurface.navPlaceholderSave-11776.test.tsx: the pillar with anAppSchema-parsing server double. Pins:Reverse check and ablations
git checkout 8273f4bof the two source files, restored fromHEAD, blob hashes equal,git diff HEADempty): 38 failed, 5 passed.accepted: falsewith{ id: 'nav_item_3', type: 'object' }in the payload.ablation-replace.mjs; anchor 1 → 0, blob moved, restored toHEAD):if (sent.unmoved() && !leftOut)→if (sent.unmoved()). The "stays on the canvas" pin fails: the rail shows onlyHome menu, Landing menuafter the re-read.setNavError(null)removed. The banner pin fails: "Service unavailable" is still shown.Gates, on
2e408c7pnpm --filter '@object-ui/app-shell^...' build: exit 0. It ran before the edits; the dependency closure is upstream of every touched file.pnpm exec vitest runon 60 files: everyStudioDesignSurface*,AppNavCanvas*andAppPreview*test,studio-locale.i18n,ResourceEditPage.navDeepLink-11153, andnavSurface.test.ts. Result:Test Files 60 passed (60),Tests 420 passed (420).pnpm --filter @object-ui/app-shell type-check: exit 0, and the script name was echoed.--listFilesOnlyshows all six touched test files are in thetsconfig.test.jsonprogram.check:control-bytescheck:new-line-citations(0 new)check:changeset-claimscheck:pending-changeset-literalscheck:vi-mock-specifierscheck:vi-mock-inheritcheck:vi-mock-override-shapecheck:test-path-rootscheck:metadata-write-doorscheck-changeset-presence(1 changeset declared)check-changeset-no-majorpnpm lint(turboeslint .per package) owns the full run. The narrowed run gave 0 errors, and its 16 warnings are exactly BASE's 16 for the two sources, by rule. The narrowing is proven by three facts:eslint .under the rooteslint.config.js;--format json;projectService,parserOptions.projector type-checked preset), so this diff cannot change a verdict in an untouched file.Deviations and fence
navPayloadOfis a module export ofAppNavCanvas.tsx, as the PM's route allowed. It does not reach the package entry:index.tsre-exports a named list, andexportshas only.. Thereact-refresh/only-export-componentsdisable follows the precedent ofrenderStudioGridList.@object-ui/typesmember, prop, orpackages/i18n/**key is added. Every file is inside the claim's surface.Acceptance notes (not carded; carrier: none)
doSave) never clears its own failure on a later landed save either. It is the same class of defect, read in source only, and outside this claim's surface.AppPreview, off-limits here) uses the same canvas "Add nav item". Whether its save sends the placeholder was not measured.role="button"span inside the card's button element. Chromium focuses it and activates it (measured). Other browser engines were not measured.Dispatched by the
domain:uiseat 1 PM; implemented in sessionhttps://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z.Generated by Claude Code