Skip to content

Studio: after 恢复原版 the rail keeps the customized mark and 仅看定制 (1) until a reload #30238

Closed

Description

@objectstack-fleet

domain:ui#1 · os-support-ai · session_01WGufzfE4V9fzZgJLrLDmMC

Category: ① product defect with a named location. ⛔ Not a claim. Found while reviewing PR #30233 (#30063), by its executor in the browser; recorded in that PR's report on #30063 (out_of_scope_findings).

Business reason. An administrator customizes a page (or a dashboard, view or the app navigation) of an installed app in Studio, then presses 恢复原版 to go back to the shipped version. The shipped item reads back, but the Interfaces rail still marks it as customized and the 仅看定制 filter still says (1). The person cannot tell whether the restore worked, and may restore again or think the customization is still live. A reload clears it.

reach: measured in the browser on examples/app-crm (dev boot, zh-CN, admin), PR #30233's head eee39383: after 恢复原版 on the customized crm_welcome page the rail keeps the mark and 仅看定制(1); a reload shows 0 marks and no filter. Screenshot: /mnt/project-files/ui-orders/30063-shots/08-restored-shipped-page-customize-offered-again.png (project files; the PR cites it).

Where. ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx: the marks and the 仅看定制 count come from usePackageCustomizations(packageId, publishNonce) (~L832), which re-reads only when publishNonce moves. restoreOriginal deletes the pair's draft and row but never moves publishNonce (or any nonce that hook reads). The path is shared by dashboard, view, list view, page and app, so it is not page-specific. The executor read this from the code; first touch should confirm on one other type (a dashboard).

Expected. After a successful restore the rail mark, the 仅看定制 count and the All metadata tile count reflect the restore without a reload.

Reader. domain:ui, one PR in studio-design/** plus a pin (restore → the customizations ledger is re-read).

Duplicate search (open and closed): restore original customization mark 32 hits, usePackageCustomizations 2 hits, 仅看定制 restore 4 hits. None records this: #29692 (closed) moved Restore onto the override mark; #29560's QA run records a different usePackageCustomizations fail (which packages it asks).

Activity

  1. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: first-touch audit · p3 · domain:ui · pm:queue · 2026-11-05T04:04Z

    ⛔ Not a claim.

    • Read fresh. After 恢复原版, the Studio rail keeps the customized mark and 仅看定制(1) until a reload, because usePackageCustomizations re-reads only on publishNonce. The data is right, and the view is stale until reload, so p3.

    Generated by Claude Code

  2. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#2 · os-bill · session_01Utp6Cb5R6JDdfyvtj3dPR2

    Claim: PM loop round 1 · 2026-11-05T07:26Z
    Session: session_01Utp6Cb5R6JDdfyvtj3dPR2 (seat); dev = card thread session_01Utp6CYbJbcjB64iwnxmHBs
    Account: os-bill
    Branch: claude/project-thread-5khb19 (harness-pinned card thread branch; the claude/issue-N-slug convention does not apply to a project card thread)
    Worktree: objectstack-issue-30238
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (restoreOriginal / customization-read region), the restore control if it owns the call, their tests, one root .changeset/ (stop on breach; explain in the report)
    Container & model: S, not mechanical (M treatment), mode:thread, model: default tier (opus) — dispatch-gates --tier: no path-derived mandate, effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — UI defect/enhancement in ui/** owned by this lane; no platform path covers it; reached by every console/Studio user
    Thread-read: 6649364709
    Premise: holds (StudioDesignSurface.tsx:834 publishNonce, :839 usePackageCustomizations; restoreOriginal not measured by the seat, the thread confirms first)
    Serial constraints cleared: seat 2 #30046 (this seat, thread cmsg_…XkTWXsjBzzss3KguAuRzTF) edits StudioDesignSurface.tsx view routing: regions disjoint, merge main before the PR

    Dispatch order: /mnt/project-files/ui-seat2/dispatch-30238.md (project files).

  3. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#2 · os-bill · session_01Utp6Cb5R6JDdfyvtj3dPR2
    os-dev-report

    {
      "issue": 30238,
      "status": "done",
      "branch": "claude/project-thread-5khb19",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30381",
      "head": "bde46d2b",
      "session": "session_01Utp6CYbJbcjB64iwnxmHBs (card thread; seat session_01Utp6Cb5R6JDdfyvtj3dPR2)",
      "premise_still_valid": true,
      "summary": "Premise held and is confirmed on a second type: restoreOriginal (and the flow and bundle-item restores) delete the pair's row and emit the metadata-refresh pulse, but the surface's usePackageCustomizations read was keyed on publishNonce only. The read is now keyed on useMetadataRefreshNonce(): restores already emit it, both publish sites emit it in the same handler as setPublishNonce (so a publish still reads once), and a copilot publish now refreshes the marks too. One key change in StudioDesignSurface.tsx (+13/-3 with comment), no new fetch path, the hook itself unchanged.",
      "tests": "New StudioDesignSurface.restoreMarks-30238.test.tsx (REAL surface + REAL metadata client over a fetch stub): dashboard from the navigation, and page under its type with a customized dashboard as CONTROL. Red on 3e7cd7f5 (test only): Tests 2 failed (2), both 'expected 1 to be greater than 1' (customization read not asked again). Green after the fix: Tests 2 passed (2). The red-first leg on the parent commit is the ablation (the fix is the only source change; tests import src, no build). All 60 app-shell test files that render the whole StudioDesignSurface: Test Files 60 passed (60), Tests 372 passed (372). turbo type-check --filter=@object-ui/app-shell: Tasks 68 successful, 68 total. eslint on the 2 changed ui files: 0 errors; StudioDesignSurface.tsx 36 warnings, same as main.",
      "gates": "dispatch-gates --commands on bde46d2b (post-merge): 35 families, all exit 0, incl. node scripts/check-sdui-manifest.mjs :: exit 0 and pnpm check:type-check-debt :: exit 0 (under the verify lock). --ran: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN. Record: /mnt/project-files/ui-seat2/30238-gates-ran.txt (project folder). CI check-runs: in_progress at report time.",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "line_budget": "3 files, +279 / -3 vs origin/main (source +13/-3, test +255, changeset +11)",
      "files_changed": [
        ".changeset/30238-studio-restore-rereads-marks.md",
        "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx",
        "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.restoreMarks-30238.test.tsx"
      ],
      "deviations": [
        "Importer tests narrowed: the 60 files that render the whole StudioDesignSurface were run; the other test files importing the module render single pillars with a host-built customization prop, which this change does not reach, and were not run.",
        "PR attribution link points at this card thread (session-context instruction).",
        "No assignee and no reviewer on the PR (dispatch Writes line); the harness post-PR assign/review step skipped."
      ],
      "mcp_calls": "3 — mcp__github__issue_read x3 (get, get_comments x2); no write tools",
      "api_writes": "2 — relay pr_create (objectstack-ai/objectstack#30381, draft, read back identical 8041 bytes), post-stamped issue comment on #30238 (this report); plus git pushes (not REST)",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "observation, not filed · the card's Expected names the All metadata tile count: DirectoryPage and ResourceListPage make their own usePackageCustomizations read on mount, and the area and its pages mount per tab and route, so after a restore elsewhere they open on a fresh read; not changed here (a copilot apply while one of them stays open would still leave it stale) · carrier: seat",
        "observation, not filed · the card lists the app navigation with the types that share the path; the navigation's rail mark reads useServedOverride keyed on servedNonce, which restoreOriginal moves, so it already cleared after a restore · carrier: none"
      ]
    }

    Report taken 2026-11-05T08:40Z.


    Generated by Claude Code

  4. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#2 · os-bill · session_01Utp6Cb5R6JDdfyvtj3dPR2

    Landed · 2026-11-05T11:46Z · PR #30381 merged as 644883ede through the merge queue (seat ACCEPT on the merged head; landing authorized by the maintainer in the seat thread, 11:29Z). Card state: closed. pm:dispatched removed.

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions