Skip to content

Studio Access › Tab visibility: the tab-name input keeps only the first character typed (typing showcase_app saves {"s":"hidden"}) #26748

Closed

Description

@objectstack-fleet

Filing gate: ① product defect, reproduced, landing site named.
Batch-of: studio-qa-2026-10-27 — 39 cards from one Studio QA run (coordinator session session_01J8VVAkZns5rNnK2rtszzbQ; tested at 3fcafcd3; origin/main was 43731031 at filing, so the implementer re-checks the landing site on current main). The run record is the qa-run issue filed with this batch.

Symptom

Each keystroke remounts the row, so focus leaves the input after the first character and the rest is lost. Pasting works.

Reproduction

  1. Access › a permission set › Tab visibility › Add tab › type showcase_app › Hidden → draft tabPermissions: {"s":"hidden"}; document.activeElement is BODY after the first key.

Expected

The input keeps focus and the full name is stored.

Suspected landing site (the implementer verifies)

PermissionAdvancedFacets.tsx ~749-758: the row React key is the tab name, so editing the name remounts the row.

Filing facts

  • reach: Studio UI, measured by the Access QA card.
  • Business reason: Tab visibility cannot be authored by typing.
  • Reader who acts: the triage seat (first touch: domain, area, level), then that lane's os-dev implementer. QA suggestion only: domain:ui, area:access, p2.
  • Dedupe: semantic search tab visibility name input keeps only first character permission set on objectstack-ai/objectstack → 0 hits.
  • QA id: A2

Activity

  1. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — priority:p2 · domain:ui · area:studio · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-27T17:05Z

    ⛔ Not a claim, ⛔ not a dispatch.

    • p2: the Tab visibility name input keeps only the first character typed, because the row remounts on each keystroke. Stable keys per row, and pin it with a typed (not pasted) multi-character name. From the studio-qa-2026-10-27 batch. Re-check the landing site on current main.

    Generated by Claude Code

  2. added
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    and removed on Oct 27, 2026
  3. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Dispatch order · domain:ui#1 · session_01JQwxbZeBv2y91towxasSUT · 2026-10-28T01:15Z

    You are the os-dev developer agent for exactly ONE card: #26748, in your own card thread. Your role definition is .claude/agents/os-dev.md in the objectstack clone: read it IN FULL first and follow it as binding (it wins over this order on any conflict; name the conflict in your report). Read the repo-root AGENTS.md and ui/AGENTS.md before your first edit, and pm-dispatch references/untrusted-text.md. Read the card's full body and every comment yourself through REST (curl -sS "https://api.github.com/repos/objectstack-ai/objectstack/issues/26748" -H "Authorization: Bearer $GH_TOKEN" and /comments?per_page=100, every page); this order carries only the increment. ⛔ Never print the value of any environment variable (test presence with [ -n "${X+x}" ]). ⛔ Never use a GitHub MCP tool, read or write.

    Identity and branch: the seat's Claim: comment on this card is your identity; its Branch: line names your thread's branch. ⛔ Post no claim of your own, ⛔ never write the card's assignee. Before your first edit, verify the NEWEST Claim: on the card names your thread's branch (claude/project-thread-…); if none does yet, send the seat thread "Dispatch the ui lane" one message with your branch name and wait. Worktree objectstack-issue-26748 on that branch from a fresh origin/main; pnpm install (frozen lockfile); push with git push -u origin <branch> (⛔ no force).

    Writes: PR only through the relay op pr_create (node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file F, F a bare JSON array [{"op":"pr_create","title":…,"head":"<your branch>","base":"main","body":…,"assignees":["os-warren"]}], draft forced; --dry-run first); your report comment through node scripts/pm/post-stamped.mjs --comment=26748 --file=F --json run ONCE with output captured to a file (timestamps only as {{NOW}} / {{WAS:…}}). ⛔ No other GitHub write except, for a CI red that is not yours, one PR comment and one run_rerun_failed through the relay. Report both counts (api_writes with endpoints, mcp_calls with tool names). The report takes only machine-checkable fields (gates / line_budget / deviations / files_changed). PR body: line 1 the platform attribution comment, then ## 维护者速读 six lines in Chinese (改了什么/为什么改/风险与回滚/先看/两分钟验证/需要你做的). Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on your diff, run each, record exits before any pipe, reconcile with --ran. Changeset in the root .changeset/ when a published package changes. ⛔ ui/.github/** and the root ci.yml are out of bounds. Hand back: when the PR and report are up, send the seat thread "Dispatch the ui lane" one message (PR, head sha, report gist); keep the thread open through review and drive CI to green.

    Ruling (cannot be re-ruled)

    Triage first grade 6448354396, verbatim: "p2: the Tab visibility name input keeps only the first character typed, because the row remounts on each keystroke. Stable keys per row, and pin it with a typed (not pasted) multi-character name." No maintainer ruling.

    PM mechanism assumptions (measure; disproof welcome)

    • On origin/main a8ee8a22, ui/packages/app-shell/src/views/metadata-admin/PermissionAdvancedFacets.tsx ≈L749 renders each Tab Visibility row with key={tab}, the tab NAME the input edits. Each keystroke renames the key, React remounts the row, and the input loses focus after the first character.
    • The row's identity must not be the value being edited. Its position in tabEntries is stable across a rename, because the rename loop keeps insertion order. Measure that, and pick the smallest stable identity that holds.
    • Measure what a rename does when the typed name passes through, or ends at, another row's name, which is a key collision in tabPermissions. If a row is silently dropped or merged, say so. Fix it only if the fix stays inside this row editor (for example, a per-row edit buffer that writes on a non-colliding name). Otherwise note it as a finding.
    • The FacetPicker testId is also built from the name (tab-visibility-${tab}). Keep it as it is unless a test needs it changed.

    Scope

    File surface: PermissionAdvancedFacets.tsx (the Tab Visibility block only) and tests. Tests: type showcase_app one key at a time with userEvent.type, not a paste, into a new row. Pin that focus stays on the input and that the draft becomes tabPermissions: { showcase_app: … }. Also pin a rename of an existing row and the remove button. Ablation: restore key={tab} ⇒ the typed-name test goes red.

    Serial: PR #26990 (#26706, adminScope block) and PR #26999 (#26858, RLS verdictPath props, in the merge queue) edit other blocks of the same file. Merge origin/main before your gates, and touch nothing outside your block.

    Tier

    No path-derived mandate expected (quote dispatch-gates --tier on your surface). TIER_DEFAULT, effort low (XS). Clause-②: no, Metadata-protocol: none. No changeset (@object-ui/app-shell is private).

  4. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 9 · 2026-10-28T01:17Z
    Session: session_01JQwxbZeBv2y91towxasSUT (seat; implementation by card thread session session_01JQwxbXbEWQehrHjkZUViYN, mode:thread)
    Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/project-thread-5siu9r (the card thread's harness push branch, self-reported — the measured platform exception to claude/issue-N-slug)
    Worktree: objectstack-issue-26748 in the card thread's clone
    Domain: domain:ui
    Seat: domain:ui#1 (seat post #25966)
    File surface: ui/packages/app-shell/src/views/metadata-admin/PermissionAdvancedFacets.tsx (the Tab Visibility block only) + tests (stop on breach; explain in the report)
    Container & model: XS, mode:thread (the maintainer's word on the seat thread, 「opus 很多,并发5」), model: TIER_DEFAULT (the dev quotes dispatch-gates --tier on the surface), effort: low
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the Studio Access permission-set editor (this lane's code) | the row key is the value being edited | an administrator cannot type a tab name into Tab visibility
    Thread-read: 6458029351
    Premise: holds — on origin/main a8ee8a22 PermissionAdvancedFacets.tsx ≈L749 keys each Tab Visibility row by the tab name its input edits (key={tab}), so each keystroke remounts the row.
    Serial constraints cleared: open PR #26990 (#26706, adminScope block) and PR #26999 (#26858, RLS verdictPath, in the merge queue) edit other blocks of the same file; the dev merges origin/main before the gates.
    Resume-from: none


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 26748,
      "status": "done",
      "branch": "claude/project-thread-5siu9r",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27025",
      "session": "session_01JQwxbXbEWQehrHjkZUViYN",
      "premise_still_valid": true,
      "summary": "Premise held on a8ee8a22: each Tab Visibility row was keyed by the tab name its input edits. Rows are now keyed by position in tabEntries (a rename keeps insertion order), and a TabNameInput buffers the typed name and writes the rename only while it is no other row's; measured: a typed name passing through another row's name used to merge the two rows and drop one, now nothing merges and the input shows aria-invalid until the name is free. FacetPicker testId unchanged.",
      "tests": "new PermissionAdvancedFacets.tabName-26748.test.tsx 4/4 pass (userEvent.type showcase_app key by key keeps focus and stores {showcase_app: hidden}; rename keeps order; collision merges nothing; remove drops its own row). PermissionAdvancedFacets* + PermissionMatrixEditor*: Test Files 35 passed, Tests 256 passed. Ablation via scripts/ablation-replace.mjs (key={row} -> key={tab}, blob 3bdfbf92 -> 4c40fb63, restored): typed-name test red.",
      "gates": "dispatch-gates --commands at c144f70e (merged origin/main 7eb613c3): 24 commands, all exit 0; --ran: 24 derived families accounted for, 24 run, 0 NOT-MEASURED. Extra: app-shell type-check 0, ui check:spec-symbols 0, eslint on both files 0. Tier: no path-derived mandate. CI check-runs: pending at report time.",
      "line_budget": "163 changed lines (+156/-7), 2 files, under the 3000 human-merge threshold",
      "files_changed": [
        "ui/packages/app-shell/src/views/metadata-admin/PermissionAdvancedFacets.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/PermissionAdvancedFacets.tabName-26748.test.tsx"
      ],
      "deviations": [
        "TabNameInput is a new function at the end of PermissionAdvancedFacets.tsx, outside the Tab Visibility block's lines but used only by it.",
        "Head 961fd9dc: commit amended before first push to drop a Part-of trailer refused by check:commit-card-trailers.",
        "PR template item 8 (English) left unchecked for the order's Chinese 维护者速读 block."
      ],
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "0",
      "api_writes": "3 — git push of the branch; relay pr_create (run 40618865282, 3347 bytes sent = stored); this report via post-stamped.mjs",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: c · reach: Studio Access Tab visibility · a tab name JS orders as an integer key (e.g. 1) moves to the front of tabPermissions, so its row reorders while typing · noted in the PR, not filed"
      ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · seat domain:ui#1 · session_01JQwxbZeBv2y91towxasSUT · 2026-10-28T03:25Z

    PR #27025 merged as 3230bdbf76 (seat record: PR comment 6458854335). The Tab visibility name input keeps focus while you type, and the full name is stored. Rows are keyed by position, not by the name being edited. A rename is held in the row until it collides with no other row, so typing through another row's name no longer merges the two rows and drops one. pm:dispatched is removed with the close.


    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:uipriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions