Skip to content

Studio Interfaces on a package with no app names it by its id ("COM.QA.RECHECK · NAVIGATION", title "com.qa.recheck") though its display name is on screen — appLabel is seeded with packageId and never replaced on the no-app branch #12718

Closed

Description

@objectstack-fleet

Filing gate ①: a user-visible defect (a machine name shown where a label exists) with a named landing and a live reproduction.

QA-source: objectstack-ai/objectstack#23953 · ux-conventions.labels-over-machine-names · observation (ux runner; re-checked and filed under objectstack-ai/objectstack#23878)

What happens

On the Interfaces pillar of a package that has no app yet, the empty state itself is fine ("This package has no app yet"). The rail heading and the pillar title next to it, though, show the package id. The package's display name ("QA Recheck") is in the top bar of the same screen. The cause is that the state holding the app label is seeded with packageId, and the load returns on the no-app branch before it ever sets a label.

Reach — measured, 2/2 fresh loads

Environment: objectstack 7277c07f (main, 2026-10-16), examples/app-showcase booted from a main worktree: OS_PORT=47321 objectstack dev --ui --seed-admin -p 47321 -d file:…/data.db, with a fresh DB. Console: objectui daeb77fc (the .objectui-sha pin), built against @objectstack/spec from a35f7a67^ (34b740f2), because the pin does not build on main (objectstack-ai/objectstack#23885 / #23908). check:console-sha matches. Playwright with /opt/pw-browsers/chromium, form sign-in as the seeded admin, and a screenshot taken before every DOM read. Code path re-read on objectui main 023087f63, where it is still present.

  1. POST /api/v1/packages {"manifest":{"id":"com.qa.recheck","name":"QA Recheck","version":"1.0.0","type":"app","namespace":"recheck"},"enableOnInstall":true} → 201. The package has no app.
  2. Open /_console/studio/com.qa.recheck/interfaces.
  3. Page text, in order: QA Recheck (top bar) · COM.QA.RECHECK · NAVIGATION (rail heading, upper-cased by CSS) · This package has no app yet. · com.qa.recheck (pillar header title) · This package has no app yet. Same on two fresh loads.
  • Expected: the rail heading and the title name the package by its label ("QA Recheck · Navigation"), or use a neutral "Navigation" while no app exists.
  • Actual: the package id.

Where (objectui main 023087f63)

  • packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx:2982: const [appLabel, setAppLabel] = React.useState<I18nLabel>(packageId);
  • :3346-3350: if (!name) { setAppStatus('missing'); return; }. This returns before setAppLabel(label).
  • :4633: the rail heading={tFormat('engine.studio.if.navHeading', locale, { app: navItemLabelText(appLabel, locale) })}.
  • :4083-4084: the pillar title when nothing is selected, navItemLabelText(appLabel, locale).
  • Two neighbouring fallbacks also end at the id: :3334-3336 (published[0].label ?? published[0].name ?? packageId) and the draft path at :3343 (label = String(d.name)).

Done when

  • With no app, the rail heading and title use the package's label (from the package list the top bar already reads) or a neutral heading, never the id. The label fallbacks above end at a human string. A test pins the no-app branch.

Reader who acts: the domain:ui execution seat (app-shell Studio). No spec contract change is needed: the package label is already served.

Dedupe

MCP search_issues on objectstack-ai/objectui, all states: "Studio interfaces empty state heading package id instead of package name" → 6 hits, all read. #7368 (PackageSwitcher raw id on fetch failure, closed) and #11196 (label-less nav entry, closed) are neighbours. #12688 is the zh-CN rail translation. None covers this seed.

Re-checked by a cloud QA session dispatched under objectstack-ai/objectstack#23878 (maintainer ruling there, 「派云卡片复核这 7 条,授权里全部立卡。有时间间隔就行」).


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, bug · priority:p3 · domain:ui · area:studio · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-16T15:15Z

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015). ⛔ Not a claim, ⛔ not a dispatch.

    • Why p3: a package id shows where its display name exists, on the no-app branch only.
    • Lane: domain:ui. Seed the app label from the package's display name on the no-app branch.
  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 Oct 16, 2026
  3. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 9, serial (priority:p3; triage grade 6218580352)
    Session: session_011K9g23W67dy2zwnyzaoB6o
    Account: os-tesla
    Branch: claude/issue-12718-studio-no-app-package-label
    Worktree: objectui-issue-12718 (in the cloud session's own container)
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: the Studio Interfaces pillar's app label, in packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (main 5e15928).

    • The appLabel state seeded with packageId (:3003).
    • The app-resolve effect, on both branches:
      • the no-app branch (:3375-3378, which returns before setAppLabel) seeds the label from the package's display name, as triage's grade directs;
      • the published and draft label fallbacks (:3363-3365, :3372) end at a human string, never at the package id while the package's display name is known.
    • The two readers are unchanged: the rail heading (:4733, through railAppLabel since objectui#12688) and the pillar title (:4180).
    • The package's display name comes from the read the top bar already makes. No new request where that read already holds it.
    • Tests beside the existing StudioDesignSurface.* families.
    • One .changeset/12718-*.md for @object-ui/app-shell.

    Outside the surface:

    • the top bar and the package switcher themselves;
    • the "This package has no app yet" empty state;
    • displayAppLabel and the served-nav reading (objectui#12688, landed);
    • every other pillar.

    A route outside the surface stops and reports (stop on breach; explain in the report).
    Container & model: S, mode:cloud, model: opus (default tier; no path-derived mandate)
    Clause-②: no
    Clause-② reason: the Studio surface draws a label it already has on screen where it drew a machine id. No spec key, prop, export or language-pack key changes, and StudioDesignSurface.tsx is not on the package entry.
    Responsibility: the Studio Interfaces pillar seeds its app label with the package id and never replaces it on the no-app branch | the top bar already reads the package's display name on the same screen | every package with no app yet (QA reproduction: a freshly installed package, 2/2 loads)
    Thread-read: 6218580352
    Serial constraints cleared: read at objectui main 5e15928. No open PR names a path on this surface: every open PR's file list was read (#12846 and #12847, plus the bot PRs). The last changes to StudioDesignSurface.tsx are PR #12792 (objectui#12707) and PR #12766 (objectui#12688), both merged. The seat holds no other card in flight (serial mode; objectui#12779 landed as 5e15928).


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 12718,
      "status": "done",
      "branch": "claude/issue-12718-studio-no-app-package-label",
      "pr": "https://github.com/objectstack-ai/objectui/pull/12849",
      "session": "session_017wgGVVegwUuFc4uhBx7Lot (cloud, dispatched by session_011K9g23W67dy2zwnyzaoB6o)",
      "premise_still_valid": true,
      "summary": "H1 confirmed at main 5e15928: InterfacesPillar seeded appLabel with packageId (:3003), and the app resolve returned on the no-app branch (:3375-3378) before calling setAppLabel. H2: the package's display name was already in the surface's own fetchPackages() writability read (:1226). That read now also keeps the package entry (id + name), and the surface passes the name to the pillar as an internal packageLabel prop, guarded by pkgEntry.id === packageId. No new request is made. The pillar now holds no app label (null) until it finds an app, and reads appLabel = loadedAppLabel ?? packageLabel ?? packageId. All three readers (rail heading via railAppLabel, pillar title, create-dialog description) are unchanged. H3: the draft-only path now takes the _drafts row's label (objectstack#22200, as the shell header's draft read at :1602 already does) and falls back to the name. The published path's `?? packageId` tail turned out to be unreachable, so it is left as it was. H4: the pillar is mounted with key={packageId}, so a package switch remounts it. The id guard stops the previous package's name from reaching the new mount for the one render before the read answers.",
      "tests": "New: packages/app-shell/src/views/studio-design/StudioDesignSurface.noAppPackageLabel-12718.test.tsx. It is a surface-level test (the real StudioDesignSurface, with fetch stubbed for /api/v1/packages), 4 tests: (1) no app: the rail reads 'QA Recheck · Navigation', the title is 'QA Recheck', the id appears nowhere in the pillar text, and /api/v1/packages is read at most once; (2) draft-only app with a label: 'Recheck Desk · Navigation', not 'recheck_app'; (3) CONTROL, published app: its own label, not the package name; (4) CONTROL, a manifest with no name: 'com.qa.recheck · Navigation'. Run: `pnpm exec vitest run <file>` gave 4 passed (4). Ablation (fix committed first at 4d148d3; the test imports ./StudioDesignSurface from source, so no dist rebuild is involved). Each leg had a trap restore `git checkout HEAD -- P`, and the anchor was checked on disk (removed-count 0, injected-count 1). Leg seed (`loadedAppLabel ?? packageLabel ?? packageId` changed to `loadedAppLabel ?? packageId`): exit 1, test (1) red, 1 failed | 3 passed. Leg draft (`rowLabel ?? String(d.name)` changed to `String(d.name)`): exit 1, test (2) red, 1 failed | 3 passed. After both legs the restored blob 9a25941fe3c5eee31e5774edc6075f267cfdb170 equals the HEAD blob and git diff HEAD is empty. Family: a grep over packages/, apps/ and scripts/__tests__ for StudioDesignSurface|appLabel|navHeading|displayAppLabel found 226 files. All ran under os-verify-lock with `pnpm exec vitest run --maxWorkers=3`: Test Files 226 passed (226), Tests 1855 passed (1855), lock VERDICT command-exit 0, lock held 624s. After merging origin/main (head 2fde2f4): the new file plus the 11181 and 12688 families gave 3 files / 18 tests passed, exit 0. LIVE: backend from e2e/live/ci/start-backend.sh (npm 10.9.4, published @objectstack/*@18.0.0-next.2, showcase e5136ff10f1a, :4718), exit 0, no traps hit. POST /api/v1/packages {id com.qa.recheck, name 'QA Recheck', no app} returned 201. Before (the backend's published /_console, and also the worktree vite console with StudioDesignSurface.tsx at 5e15928): innerText 'COM.QA.RECHECK · NAVIGATION' … 'com.qa.recheck'. After (worktree vite console with the fix): 'QA RECHECK · NAVIGATION' … 'QA Recheck'. PNGs and a README are on claude/issue-12718-evidence@d98fcb6 under evidence/12718/. NOT MEASURED: a live reading of a draft-only app, reason: not attempted (no draft-app write route was set up in this session). The unit test covers it.",
      "gates": "Local, exit codes captured before any pipe. On 4d148d3/a0dcbe3: `pnpm --filter '@object-ui/app-shell^...' build` 0; `pnpm --filter @object-ui/app-shell type-check` 0 (echoed `tsc --noEmit && tsc -p tsconfig.test.json`); `pnpm check:changeset-claims` 0; `pnpm changeset:check` 0; `pnpm check:test-path-roots` 0; `pnpm check:spec-symbols` 0; `pnpm check:new-line-citations` 0 (VERDICT 0 new citations); `node scripts/check-changeset-presence.mjs` 0 (2 source files of 1 released package, 1 changeset); eslint on the two changed files 0 (0 errors, 13 warnings, all on pre-existing lines such as 1230 setPkgWritable and 3487 exhaustive-deps; none on added lines). Re-run on the merge head 2fde2f4: type-check 0, check-changeset-presence 0, check:changeset-claims 0. i18n gates were not run because no copy or key changed. The --ran reconciliation is not applicable: objectui has no dispatch-gates.mjs, so the list was derived by hand from package.json/ci.yml, as the brief directs. CI families: no PR exists yet, so no check-runs to cite. The seat reads them once the draft PR is open.",
      "mcp_calls": "3 — mcp__github__issue_read (get, get_comments) and ToolSearch loads; all reads, no write tool",
      "api_writes": "0 — the only GitHub writes were git pushes: the branch claude/issue-12718-studio-no-app-package-label (empty probe at 5e15928, then 2fde2f4) and the evidence branch claude/issue-12718-evidence (d98fcb6)",
      "open_questions": [
        {
          "question": "AppSchema.label is REQUIRED in @objectstack/spec, so a label-less app is spec-invalid. Even so, the pillar's existing fallbacks (list row `label ?? name`, body `label ?? name`) end at the app's machine name, not the package's display name. Should a label-less app fall to the package name instead?",
          "options": [
            "A: leave as is. Label-less apps are spec-invalid, the fallback is a consumer tolerance the four axes say not to extend, and the shell header (:1602) uses the same label-then-name order",
            "B: insert the package name before the app name in both chains"
          ],
          "recommendation": "A. Contract-first: the spec already requires the label, so B would add a tolerance tier for metadata that publish should refuse anyway, and it would make the pillar disagree with the shell header."
        },
        {
          "question": "The surface's writability read does not listen to `objectui:packages-changed`; only the top bar's switcher does. After a package rename, the no-app heading keeps the old name until a remount, while the top bar updates. Follow up?",
          "options": [
            "A: no. This is outside the claim's surface, a rename of a no-app package while it is open is rare, and any navigation remounts the pillar",
            "B: make the :1226 read refetch on that event too"
          ],
          "recommendation": "A for this card. It is noted in Acceptance notes; it is not a filed defect."
        }
      ],
      "out_of_scope_findings": [
        "carrier: none — noted, not filed: a rename made elsewhere does not refresh the Interfaces no-app heading until a remount (the writability read has no `objectui:packages-changed` listener; the top bar does). This is an observation from reading the code and was not reproduced, so it goes to Acceptance notes only."
      ]
    }

    Posted by the domain:ui seat 1 PM (session_011K9g23W67dy2zwnyzaoB6o) verbatim from the cloud session's final message, because a cloud session cannot comment from its container. The seat filled pr with the draft PR it opened, #12849.

    The seat's answers:

    • Open question 1 (a label-less app): A, leave the order. The spec requires an app's label, so a label-less app is metadata publish should refuse. A package-name tier before the app name would add tolerance for it, and the pillar would disagree with the shell header.
    • Open question 2 (refetch on objectui:packages-changed): A, no follow-up. It is outside the claim, unreproduced, and any navigation remounts the pillar. It stays in the Acceptance notes.
    • The seat read the diff at 2fde2f4: 3 files, +215/−3, all on the claimed surface.
      • The head's diff from its merge-base equals the fix and changeset commits line for line; the merge 2fde2f4 brings only main.
      • The display name comes from the surface's existing fetchPackages() answer, held with its id and passed only when that id is the current package's. No request is added.
      • The pillar reads app label, then package name, then package id. The draft path reads the _drafts row's label.
      • Clause-②: no stands.
    • The seat's edit: the dev's footer line in the PR body is replaced with the provenance paragraph. Next: CI on 2fde2f4, then the ACCEPT.

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:ui seat 1 · session_011K9g23W67dy2zwnyzaoB6o · 2026-10-17T03:39Z

    • Merged: PR fix(app-shell): Studio Interfaces names a package with no app by its display name (objectui#12718) #12849 landed through the merge queue as main e1351a8e3c, with one parent, b921f90. It is an ancestor of origin/main.
    • Verified by content: the merge commit's own diff equals the PR's diff from its merge-base 0812a19 to the reviewed head 2fde2f4, changed line for changed line in each of the 3 files (+215/−3). Positive control at the merge commit: StudioDesignSurface.tsx:3020 reads const appLabel: I18nLabel = loadedAppLabel ?? packageLabel ?? packageId;.
    • Card: closed completed by Fixes #12718. This note precedes the strip of pm:dispatched, and the cloud session is archived.
    • Next, outside this card: none filed. The missing refetch on objectui:packages-changed stays in the PR's Acceptance notes.

    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:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions