Skip to content

Studio Interfaces: an in-progress navigation rename does not raise the beforeunload guard, so a reload or tab close silently loses it #27464

Closed

Description

@objectstack-fleet

domain:devx#1 · sunhaolin · session_017xRBcVkGJizGH7raJHhAvL

Filing gate: ① a product defect, a QA run clause fail (studio-authoring.held-edit-leave-guard acceptance[1]).

Reproduction

  1. Boot the showcase. Create a writable package with an app whose navigation has a dashboard entry labelled "QA Dash".
  2. Open /_console/studio/PKG/interfaces, click "Edit" (nav editing opens and the "Done" chip appears), double-click the "QA Dash" entry, and type "QA Dash Lost" into the inline input.
  3. Reload the page, or close the tab.
  4. Expected: the browser's beforeunload prompt. Every other held or dirty Studio pillar raises it: Data picklist/lookup, Automations step, Interfaces widget, Access tick before autosave.
  5. Actual: no prompt. In the page, window.dispatchEvent(new Event('beforeunload',{cancelable:true})) leaves defaultPrevented false. The reload proceeds, the label reverts to "QA Dash", and no PUT was sent.

In-app departures in the same state (pillar link, Studio home) do show the "You have unsaved changes. Leave this page anyway?" confirm, so the surface knows the edit is unsaved. Only the document-level guard is missing. Reproduced twice on fresh contexts, plus four timing cells (150 ms and 7 s). Positive control: the same probe returns true with a held Data picklist.

Mechanism

StudioDesignSurface.tsx (~L1585–1597) intends the leave guard to cover the "Interfaces nav editor". The in-app guard sees the nav editor's dirty state, but the beforeunload registration does not include it.

QA-source: #27441 · studio-authoring.held-edit-leave-guard · acceptance[1]


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: studio | studio-authoring.held-edit-leave-guard | P①

    Triage: p2 · domain:ui · area:studio · 2026-10-29T07:59Z

    ⛔ Not a claim.

    • Read fresh. A clause fail: an in-progress navigation rename in Interfaces does not raise beforeunload, so a reload loses it, while every other held Studio pillar raises it. From the 18.3.0 RC run ([sweep] v18.3.0 release test sweep #27377 §5); it is retested on the next wave.
    • ⛔ It uses the same held-edit guard the other pillars use. It does not get a second listener.
    • bug, pm:queue.

    Generated by Claude Code

  2. added
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    bugSomething isn't working
    and removed on Oct 29, 2026
  3. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx

    Claim: PM loop round 1 · 2026-10-29T11:32Z
    Session: session_01L6bhgXU26UBEjd1KKHgNEx (seat; in-seat mode:subagent dev, same session, identity by branch)
    Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-27464-interfaces-rename-held-guard
    Worktree: objectstack-issue-27464
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: ui/packages/app-shell/src/views/metadata-admin/previews/AppNavCanvas.tsx (report the uncommitted rename), ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (wire it into the existing held-edit report only), metadata-admin/previews/AppPreview.tsx (the other AppNavCanvas caller, prop default), studio i18n only if a held clause string is needed, tests beside, one patch changeset (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: TIER_DEFAULT (seat-run dispatch-gates --tier on the paths: no path-derived mandate), effort: medium
    Clause-②: no
    Metadata-protocol: none
    Responsibility: Studio Interfaces pillar (this lane) | the held-edit guard every other pillar uses (useHeldEditReport / onHeldEditChange, StudioDesignSurface.tsx ~461–470, beforeunload ~1576–1598) | every Studio author renaming a navigation entry who reloads or closes the tab before blur/Enter
    Thread-read: 6491248737
    Premise: holds — on origin/main 57c51aab4d, beforeunload is registered when pillarDirty || heldEdit !== null, but the in-progress rename is local draft state in AppNavCanvas.tsx (~608, commit on blur/Enter ~664–672) and sets neither, so a reload loses it.
    Serial constraints cleared: no open PR changes these files (seat read of every open PR's ui file list at claim time); Studio neighbours dispatched this round (#27518 dataset pickers, #27458 / #27464) share only ui/packages/i18n/src/namespaces/studio.
    Resume-from: none

  4. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx

    os-dev-report

    {
      "issue": 27464,
      "status": "done",
      "branch": "claude/issue-27464-interfaces-rename-held-guard",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27557",
      "session": "session_01L6bhgXU26UBEjd1KKHgNEx",
      "premise_still_valid": true,
      "summary": "Premise re-measured on origin/main 6892888e: the surface's beforeunload reads unsavedPillarWork = pillarDirty || heldEdit, and the in-progress rename lived only in NavCard's local draft. AppNavCanvas gains an optional onRenamePendingChange(label | null) (reported while the input differs from the authored label, cleared on commit, Escape, typed-back text and unmount, only by a card that reported). The Interfaces pillar folds it into its one existing useHeldEditReport call (leaf clause first, then the new engine.studio.held.navRename clause, en + zh), so the existing guard fires: no second listener. AppPreview is untouched (prop optional).",
      "tests": "head 301cf9ff: pnpm --dir ui exec vitest run packages/app-shell/src/views/studio-design/StudioDesignSurface.navRenameHeld-27464.test.tsx -> Test Files 1 passed, Tests 4 passed (lock VERDICT command-exit 0). Related at e814dd17 (pre test-only trim): metadata-admin/previews/, studio-design nav*/held*/pillarNavGuard/InterfacesPillar*, packages/i18n/ -> Test Files 271 passed, Tests 3248 passed, 13 skipped (VERDICT command-exit 0). type-check @object-ui/i18n and @object-ui/app-shell: exit 0 (both script names echoed). Ablation via scripts/ablation-replace.mjs deleting the onRenamePendingChange={setNavRenamePending} wiring in StudioDesignSurface.tsx: anchor 1 -> 0, blob b47faa46 -> 2dc39a60, 3 failed | 1 passed (the three guard cases red, the AppPreview-shape control green); restore blob == HEAD, git diff HEAD empty. No build/dist leg: the test imports app-shell src directly.",
      "gates": "local: dispatch-gates --commands at 301cf9ff derived 34; all 34 run, each exit 0 (33 check/node commands + pnpm --dir ui exec vitest run scripts/ -> 169 passed | 2 skipped files, VERDICT command-exit 0); --ran with exit codes: 34 derived, 34 run, 0 NOT-MEASURED (derived zero). pnpm --dir ui run check:new-line-citations: 0 new, exit 0. CI on PR #27557 head 301cf9ff at report time: 20 check-runs, 17 in_progress, 3 skipped, none concluded; not re-run locally.",
      "line_budget": "196 changed lines (+195 / -1, 6 files) vs the 200 ceiling",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "0",
      "api_writes": "2 — fleet-write relay pr_create (repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches -> PR #27557, draft, no assignees); post-stamped issue comment POST /repos/objectstack-ai/objectstack/issues/27464/comments. git push not counted.",
      "deviations": "PR body footer uses the session-URL form (os-dev.md: session-URL footer for PR bodies) where the dispatch order spelled the bare form; os-dev.md wins on conflict. The dispatch-mandated first-line attribution HTML comment is kept as ordered. AppPreview.tsx not edited (the new prop is optional, so no default was needed). First check:new-line-citations attempt used the root script name, which does not exist (exit 254); re-run from ui/ as documented.",
      "files_changed": [
        ".changeset/27464-interfaces-rename-held-guard.md",
        "ui/packages/app-shell/src/views/metadata-admin/previews/AppNavCanvas.tsx",
        "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx",
        "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.navRenameHeld-27464.test.tsx",
        "ui/packages/i18n/src/namespaces/studio/en.ts",
        "ui/packages/i18n/src/namespaces/studio/zh.ts"
      ],
      "open_questions": [],
      "out_of_scope_findings": []
    }
  5. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx

    落地 · 2026-10-29T20:25Z

    读数 结果
    PR #27557 MERGED 2026-10-29T20:25Z,合并提交 c47f5ebc9b 在 origin/main 上
    git grep -c "navRename" origin/main -- ui/packages/i18n/src/namespaces/studio/en.ts 1(阳性对照 celInvalid,即 main 上同位置的另一行 = 3)

    状态:卡已由 PR 关闭,摘 pm:dispatched。排在本卡之后的 #27459 现可派,收班时留在队列。

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