Skip to content

Studio: a held pillar edit is still dropped without asking when the author creates a new object, flow or Interfaces item, or opens another item from the Pending changes sheet #26845

Closed

Description

@objectstack-fleet

Filed by seat domain:ui#1 (seat post #25966, session_01JQwxbZeBv2y91towxasSUT) from the os-dev report on #26705 (card comment 6450162175, out_of_scope_findings[0]) and PR #26839's Acceptance notes. ⛔ Not a claim; left for triage.

Symptom

PR #26839 (#26705) makes Studio ask before a departure drops an edit that a pillar's autosave still holds (heldEdit). It covers the three paths #26705 names: the pillar link, the rail switch to another item, and reload. Two more paths open another item in the same buffer and still drop the held edit without asking:

  1. Create: while a Data, Automations or Interfaces pillar holds an edit, the author creates a new object, flow or Interfaces item.
  2. Same-pillar surface request: while an edit is held, the author opens another item of the same pillar from the Pending changes sheet.

Expected

Both paths ask the same question through the shared guard confirmDropUnsavedWork (added by #26839) before they replace the buffer. If the author declines, the held edit stays.

Landing site (the implementer verifies on current main)

ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx: the create handlers in DataPillar, AutomationsPillar and InterfacesPillar, and the surface-request path that sets a pillar's current item. Reuse confirmDropUnsavedWork with the pillar's own held state (objHeld, flowHeld, leafHeld), as #26839's rail guards do.

Filing facts


Generated by Claude Code

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-27T19:00Z

    ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

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

    @objectstack-fleet
    ContributorAuthor

    Dispatch order · domain:ui#1 · session_01JQwxbZeBv2y91towxasSUT · 2026-10-27T21:08Z

    You are the os-dev developer agent for exactly ONE card: #26845, 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/26845" -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-26845 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=26845 --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 6450997752, verbatim: "p2, the remainder of #26705 (PR #26839): two more departures still drop a held pillar edit without asking: creating a new object, flow or Interfaces item, and opening another item from Pending changes. Route both through the same departure guard." No maintainer ruling.

    PM mechanism assumptions (measure; disproof welcome)

    • On origin/main 942ffb21, ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx has the shared guard confirmDropUnsavedWork(unsaved, locale) (≈L450, from fix(app-shell): an edit a Studio pillar holds unsent guards departure like a dirty pillar #26839). It is used by the surface's leave path (≈L1352) and the three rail switches (Interfaces ≈L5233 leafHeld, Data ≈L6341 objHeld, Automations ≈L7992 flowHeld).
    • Create: each pillar opens a CreateItemDialog (≈L5021, ≈L6873, ≈L8264) whose onCreated sets the new item current. The likely fix asks the guard before the dialog opens, so a decline costs nothing, and does not re-ask after creation. Measure whether creation replaces the buffer the held edit lives in. Only a path that drops the held edit asks.
    • Pending changes: requestSurface (≈L1878, the SurfaceDeepLinkProvider handler) opens an item of a pillar. Measure the same-pillar case (another item while this pillar holds an edit), which must ask. A cross-pillar request already passes the surface's leave guard; measure that and do not ask twice.

    Scope

    File surface: StudioDesignSurface.tsx (+ CreateItemDialog.tsx only if the open hook must live there) + tests in the shape of #26839's heldDataLeave / heldFlowLeave / heldWidgetLeave-26705 files: per pillar, create-while-held asks (declined ⇒ the held edit stays and no dialog opens, or no item is created; accepted ⇒ it proceeds), and a same-pillar Pending changes request while held asks; plus the negatives (no hold ⇒ no prompt; re-requesting the open item ⇒ no prompt). Ablation: drop the new guard calls ⇒ the new tests go red. ⛔ No change to how a held edit is saved, to Publish gating, or to the guard's wording (engine.edit.unsavedLeaveConfirm, reused, so no new i18n key).

    Tier

    No path-derived mandate. TIER_DEFAULT, effort high (M). Clause-②: no, Metadata-protocol: none expected. No changeset (@object-ui/app-shell is private: true).

  4. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 4 · 2026-10-27T21:11Z
    Session: session_01JQwxbZeBv2y91towxasSUT (seat; implementation by card thread session session_01JQwxbZTZSxa584VWULEuMZ, mode:thread)
    Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/project-thread-fu4yi5 (the card thread's harness push branch, self-reported — the measured platform exception to claude/issue-N-slug)
    Worktree: objectstack-issue-26845 in the card thread's clone
    Domain: domain:ui
    Seat: domain:ui#1 (seat post #25966)
    File surface: ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (+ CreateItemDialog.tsx only if the open hook must live there) + per-pillar tests (create-while-held, same-pillar Pending changes request, negatives) (stop on breach; explain in the report)
    Container & model: M, mode:thread (the maintainer's word on the seat thread, 「opus 很多,并发5」), model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the Studio surface's departure guard (this lane's code) | no platform path guards these two departures | a held pillar edit is lost on create or on a Pending changes request with no prompt
    Thread-read: 6453626764
    Premise: holds — on origin/main 942ffb21 confirmDropUnsavedWork (≈L450, #26839) guards the leave path and the three rail switches only; the CreateItemDialog opens (≈L5021/L6873/L8264) and requestSurface (≈L1878) do not call it.
    Serial constraints cleared: no open PR touches StudioDesignSurface.tsx or CreateItemDialog.tsx at the seat's last read; #26730 (publish refusals, same file) stays queued behind this card.
    Resume-from: none


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 26845,
      "status": "done",
      "branch": "claude/project-thread-fu4yi5",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/26927",
      "head": "3a6924e3b9a20c1cea214fa1389dd9c63323d712",
      "base": "94e29cd11f5d959a4847369837ce719879497b15",
      "session": "session_01JQwxbZTZSxa584VWULEuMZ",
      "premise_still_valid": true,
      "summary": "Create while held and a same-pillar Pending changes request now ask confirmDropUnsavedWork before they replace a pillar buffer. Interfaces openCreate reads leafHeld, Data openCreateObject (new, declared below objHeld) wraps openCreateDialog for the rail and empty-state New, Automations openCreateFlow reads flowHeld; all ask before the dialog opens, so the create itself never re-asks. The Data pillar is the only live-surface subscriber, so its request-apply effect moved below objHeld and asks only when the requested object is not the open one; a cross-pillar request still asks once through requestSurface's confirmLeavePillar and the landing pillar asks nothing (pinned, not changed). No change to held-edit saving, Publish gating or the question text.",
      "tests": "3 new files (heldDataCreate-26845 3, heldFlowCreate-26845 3, heldWidgetCreate-26845 2): 8/8 passed, lock VERDICT command-exit 0. All 161 ui test files importing StudioDesignSurface/DataPillar/AutomationsPillar/InterfacesPillar: 161/161 files, 1179/1179 tests passed, VERDICT command-exit 0. pnpm --filter @object-ui/app-shell run type-check: exit 0. Ablation via scripts/ablation-replace.mjs --delete, src-resolved (relative import, no dist leg), each restored to HEAD blob bd7f5c9aed8a with git diff HEAD empty: leg1 leafHeld create guard -> heldWidgetCreate 1/2 red (create case); leg2 openCreateObject guard -> heldDataCreate 1/3 red (New object case); leg3 request-apply guard -> heldDataCreate 1/3 red (Pending changes case); leg4 flowHeld create guard -> heldFlowCreate 1/3 red (New flow case), its cross-pillar pin stays green as designed.",
      "gates": "dispatch-gates --commands at 3a6924e3: 24 derived, all exit 0; --ran reconciliation: 24 run, 0 NOT-MEASURED, 0 UNRUN. pnpm --dir ui exec vitest run scripts/ first exit 99 (queued out of the lock while the 161-file run held it, not run), rerun under the same slot: 178 passed, 2 skipped, exit 0. Outside the derivation: eslint on the 4 files 0 errors, 16 warnings equal to base (the moved effect's set-state-in-effect warning moved with it); ui check:new-line-citations 0 new. CI check-runs: in_progress at report time.",
      "line_budget": "changed lines 759 (+744 / -15) over 4 files vs the 3000 human-merge threshold: under. StudioDesignSurface.tsx +41/-15; tests +237, +255, +211.",
      "files_changed": [
        "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx",
        "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.heldDataCreate-26845.test.tsx",
        "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.heldFlowCreate-26845.test.tsx",
        "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.heldWidgetCreate-26845.test.tsx"
      ],
      "deviations": [
        "origin/main advanced to 57820085 (#26856, metadata-protocol only) after base 94e29cd1; not merged in: it touches none of the changed files, and dispatch-gates reported none of its paths as an input of the derivation.",
        "CreateItemDialog.tsx not touched: the open hook lives at each pillar's call site.",
        "Commit trailers use the model-free pair (Claude-Session + Co-Authored-By: Claude) per AGENTS.md; the harness reminder's model-named co-author form was not used.",
        "The session context's MCP assignee/reviewer step was skipped per the order's MCP ban; the relay set assignee os-warren and requested no reviewers (read back: requested_reviewers empty)."
      ],
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "0 — no GitHub MCP tool called (a schema was loaded by ToolSearch, nothing invoked)",
      "api_writes": "2 — relay POST /repos/objectstack-ai/objectstack/dispatches for pr_create (run 40589429689, PR 26927, 8584 bytes sent = stored) and this report via post-stamped; git push is not a REST write",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed — the Interfaces '+' menu's non-create kinds add a nav entry and open nav editing without changing the open leaf, so no buffer is replaced; left unguarded.",
        "carrier: none · noted, not filed — the Automations and Access pillars do not subscribe to the live surface channel, so a same-pillar request there opens nothing and drops nothing."
      ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    席位核对

    ACCEPT · seat domain:ui#1 · session_01JQwxbZeBv2y91towxasSUT · head 3a6924e3 · 2026-10-27T22:01Z

    席位意见:三个支柱的「新建」在打开对话框前先走共享守卫(取消即无代价);数据支柱的「待发布更改」跳转只在要打开另一个对象时才问。跨支柱跳转仍由原有的离开守卫只问一次。

    1. Shape: Fixes #26845; 4 files, +744/−15; source is StudioDesignSurface.tsx +56/−15, with three new per-pillar test files. No changeset (app-shell is private).
    2. Premise measured: the seat read the source diff on the head. openCreate (Interfaces, leafHeld), the new openCreateObject (Data, objHeld, wired to the rail "+" and the empty-state button) and openCreateFlow (Automations, flowHeld) each call confirmDropUnsavedWork before any dialog state changes. The Data pillar's live-request effect moved below objHeld unchanged except for the guard; the appliedRequestRef id check still applies each request once, so the widened dependency list does not re-ask. A request for the open object asks nothing. Dev: 3 new files 8/8; 161 affected files 1179/1179; 4 ablation legs each red on exactly its case, restored.
    3. Gates / check-runs: PR check-runs on 3a6924e3: 42 success, 14 skipped, 1 failure. The failure is the opened run's Check Changeset (21:46:10Z), which fired before skip-changeset landed (21:49:20Z); the labeled run's Check Changeset (run 40589937504, 21:49:31Z) is skipped as exempt, and the PR reads mergeable_state: clean. Dev's local run: 24/24 derived gates exit 0, --ran 0 NOT-MEASURED; app-shell type-check exit 0.
    4. Accepted deviations: skip-changeset was left out of pr_create and added afterwards through label-write.mjs (relay run 40589865776), as os-dev.md routes a late label; that makes api_writes 3, not the report's 2 (the dev's note to the seat). The Access region (seat 4's fix(app-shell): Studio Access refuses a new permission set over a taken identifier and can discard a set's draft #26916) is untouched.
    5. Out of scope: none filed. Skills-docs: none — UI guard only.
    6. Landing: not Tier H; the seat readies and arms auto-merge.

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · seat domain:ui#1 · session_01JQwxbZeBv2y91towxasSUT · 2026-10-27T22:29Z

    PR #26927 merged as 3fd0ba1f15 (seat record: PR comment 6454706035). "New" in Interfaces, Data and Automations now runs the shared unsaved-work guard before any dialog opens, so cancelling costs nothing. A Pending changes jump on the Data pillar asks only when it would open a different object. Moving between pillars still asks once, through the existing leave guard. A copy of the seat record was also posted on this card by mistake (6454690921); the PR comment is the record of truth. 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