Skip to content

Studio dashboard Global Filters: a partly filled "Options From" (object set, value and label fields not yet) autosaves, is refused 422, and every later dashboard edit is refused and lost on reload #29006

Closed

Description

@objectstack-fleet

domain:devx#1 · os-warren · session_01JQwxbX9avDwM2goWd7zimE

Filing gate: ① a product defect, a QA run clause fail (studio-authoring.dashboard-widget-and-filter-types acceptance[4]).
Business reason: a dashboard author who adds a select global filter and fills its "Options From" object first sees "Changes not saved: the server refused this draft", and every edit made after that (label, description, other widgets) is refused too and silently lost on reload. #27459 fixed the empty-item case; this is the partly filled case of the same promise ("the editor never autosaves an incomplete filter").
Reader who acts: the triage seat (first touch), then a domain:ui implementer in ui/packages/app-shell/src/views/studio-design/. QA suggestion only: area:studio, p2.
Duplicate search: listItemHeldInput → 2 hits (#27459 closed, #27785 closed: the empty-item and report-list siblings, neither covers a nested object input). optionsFrom global filter Studio → 14 hits, all QA run records or the sweep anchor.
Batch-of: release-18.3.0-w4-c3b1 — 16 cards from QA run #29002 (subject 7fa9873f; the implementer re-checks the landing site on current main).

Reproduction

  1. Boot the showcase. Use a writable package with an app whose navigation names a dashboard with one metric widget (QA used package com.qa.r3, dashboard r3_hold on dataset r3_ticket_metrics).
  2. Open /_console/studio/PKG/interfaces, select the dashboard → Properties → Filters → Global Filters → "Add item". No PUT is sent (held, as designed after Studio: "Add item" on a dashboard list property autosaves an empty item (422), and every later edit is refused and lost until reload #27459).
  3. Fill Field (region), then set Input Type = select. Each step sends PUT /api/v1/meta/dashboard/r3_hold?mode=draft&package=PKG&surface=studio → 200.
  4. Type r3_ticket into Options From → Object (#mdf-globalFilters.N.optionsFrom.object) and press Tab. The PUT body carries "optionsFrom":{"object":"r3_ticket"} → 422 INVALID_METADATA, issues globalFilters.N.optionsFrom.valueField [invalid_type] and …labelField [invalid_type].
  5. Edit the dashboard Description, then its Label. Each PUT carries the same incomplete item and is refused 422.
  6. Reload. No leave prompt (new Event('beforeunload',{cancelable:true}) leaves defaultPrevented false). The Description and Label edits are gone, and the draft read shows neither.

Expected: the partly filled item is held like the empty one (or the refused edits are guarded on leave), and no other edit is lost. Actual: as above. Reproduced 3 times on fresh loads.

Mechanism (suspected; the implementer verifies)

listItemHeldInput() in ui/packages/app-shell/src/views/studio-design/metadataError.ts holds an item only when every issue names an unset input directly below the item (input = path[at + 1]). For globalFilters.N.optionsFrom.valueField that input is optionsFrom, which already holds {object}, so the item is classed "finished but wrong" and sent. The surface guard in StudioDesignSurface.tsx (leafIncomplete → useDraftAutoSave blocked) never engages, and nothing arms beforeunload for a refused draft.

Contract: GlobalFilterSchema.optionsFrom (packages/spec), unchanged; the fix is client-side.

QA-source: #29002 · studio-authoring.dashboard-widget-and-filter-types · acceptance[4]

Activity

  1. added
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    domain:uiDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    on Nov 2, 2026
  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: studio-authoring.dashboard-widget-and-filter-types

    Triage: domain:ui · p2 · pm:queue · 2026-11-02T07:28Z

    ⛔ Not a claim.

    • Read fresh. listItemHeldInput in ui/packages/app-shell/src/views/studio-design/metadataError.ts only holds an item whose unset input sits directly below it, so a partly filled optionsFrom is sent, refused 422, and later edits are lost; domain:ui. Silent loss of author edits is a user-visible defect, p2. The FAIL is on a P2 item, so the release rule does not apply.

    Generated by Claude Code

  3. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 2, 2026
  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#2 · zhuangjianguo · session_01QsR2jRFuJz9QBZqPVHYVhJ

    Claim: PM loop round 4 · 2026-11-02T10:09Z
    Session: session_01QsR2jSRsNRkCr8sZjG6Cpz (card thread, os-dev); dispatched by seat session session_01QsR2jRFuJz9QBZqPVHYVhJ
    Account: zhuangjianguo
    Branch: claude/project-thread-87tl4w (platform-assigned to the card thread; it can push only this branch)
    Worktree: objectstack-issue-29006
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: ui/packages/app-shell/src/views/studio-design/metadataError.ts (listItemHeldInput and its doc comment only), its tests, and a root .changeset; not StudioDesignSurface.tsx (stop on breach; explain in the report)
    Container & model: S, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: default
    Clause-②: no
    Metadata-protocol: none
    Responsibility: a dashboard global filter with a partly filled Options From is autosaved, refused 422, and every later edit is refused and lost on reload | silent loss of author edits; release checklist studio-authoring.dashboard-widget-and-filter-types acceptance[4] fails | every Studio dashboard author
    Thread-read: none
    Premise: holds (card measured at 7fa9873f; at origin/main 45e9b2f1, listItemHeldInput (~L1130) holds an item only when its unset input sits directly below the item; blocker PR #29143 landed 09:58Z as 1e4841a2; no open PR touches metadataError.ts; GlobalFilterSchema.optionsFrom unchanged, fix is client-side)
    Serial constraints cleared: StudioDesignSurface.tsx is held by #28984 (seat 2), #29113 and #29148 (seat 5) and PR #29224 (seat 3), and is outside this surface

  5. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#2 · zhuangjianguo · session_01QsR2jSRsNRkCr8sZjG6Cpz
    os-dev-report

    {
      "issue": 29006,
      "status": "done",
      "branch": "claude/project-thread-87tl4w",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29300",
      "head": "36e3a484",
      "session": "session_01QsR2jSRsNRkCr8sZjG6Cpz",
      "premise_still_valid": true,
      "summary": "listItemHeldInput now walks each issue's path down from the list item (objects and list indexes) and holds the item when it meets a value left unset (absent or blank text) at any depth; a step through a value the author gave meets none. A Global Filter with optionsFrom {object} only is held as the empty item is, named by its form row 'Options From'; a set nested value the spec refuses still sends the item. Live on the showcase: main sent 422 at Options From -> Object, Description and Label, no leave guard, edits lost on reload; this branch sends no PUT, shows the held line, arms the leave guard, and one PUT 200 carries every edit once Value Field and Label Field are set.",
      "files_changed": [
        "ui/packages/app-shell/src/views/studio-design/metadataError.ts (listItemHeldInput body and its doc comment only; +19 -3)",
        "ui/packages/app-shell/src/views/studio-design/metadataError.heldNestedInput-29006.test.tsx (new, 251 lines)",
        ".changeset/29006-held-nested-list-input.md (new; @object-ui/app-shell patch; Clause-② no; Metadata-protocol none)"
      ],
      "line_budget": "diff vs origin/main: 3 files, +277 -3",
      "tests": "new file 8/8 on 6399fd66; ablation (scripts/ablation-replace.mjs, direct-input check put back, anchor 1->0, blob 60f02752->63e45cd0) with the 27459 unit file: 3 failed (two nested held units + pillar case) / 20 passed, restored blob == HEAD and git diff HEAD empty; neighbour sweep (21 app-shell files naming metadataError / held-edit functions / heldListItem) 202/202 on 7559e61b and again on 36e3a484; turbo type-check app-shell 65/65 exit 0; eslint both files exit 0. Narrowing: whole app-shell suite left to CI Test UI.",
      "gates": "dispatch-gates --commands on 7559e61b: 34 derived, all exit 0 (incl. check-sdui-manifest, check-changeset-no-major, check-adr-0087-registration, check:type-check-debt locked, ui vitest run scripts/ 169 passed 2 skipped locked); --ran: 34 derived, 34 run, 0 NOT-MEASURED. Body gates against the PR body: changeset-no-major 0, adr-0087-registration 0, partof-closing-keyword 0, closing-keyword-parity --body binds only 29006. PR check-runs: in_progress at report time (seat re-runs).",
      "measurement": "/mnt/project-files/issue-29006/measure.md (run record, JSON, screenshots; not on GitHub)",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "deviations": [
        "Dispatch wording said the held outcome is 'the draft autosave leaves the item out and the other edits save'. The existing hold (unchanged surface) sends nothing while held and sends every kept edit once the item is finished; the surface test pins that, as the ruling's 'hold exactly as the empty one is held' intends.",
        "Surface test lives in the same new file as the unit cases (one new test file beside the metadataError tests).",
        "PR has no assignee and no reviewers (common rule 10)."
      ],
      "mcp_calls": "0",
      "api_writes": "2 — relay pr_create (objectstack#29300); this post-stamped comment on #29006. Reads were gh api GETs (issue, comments, PR 29143). git push is not a REST write.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: none (wording) · HeldListItem.input doc ('the item's input the spec refuses it without') and the objectstack#27459 section header still read as direct-input wording; outside the claimed surface · carrier: seat · noted, not filed"
      ]
    }
  6. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#2 · zhuangjianguo · session_01QsR2jRFuJz9QBZqPVHYVhJ

    Landed · 2026-11-02T12:27Z · PR #29300 merged through the queue as 3ec26471; the merge commit carries all 3 files of the accepted head (the listItemHeldInput walk below the item in metadataError.ts, its test, the changeset). Delivered in full: nothing remains on this card; pm:dispatched removed.

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 portaldomain:uiDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions