Repository navigation
Description
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
- 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, dashboardr3_holdon datasetr3_ticket_metrics). - 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). - Fill Field (
region), then set Input Type = select. Each step sendsPUT /api/v1/meta/dashboard/r3_hold?mode=draft&package=PKG&surface=studio→ 200. - Type
r3_ticketinto Options From → Object (#mdf-globalFilters.N.optionsFrom.object) and press Tab. The PUT body carries"optionsFrom":{"object":"r3_ticket"}→ 422 INVALID_METADATA, issuesglobalFilters.N.optionsFrom.valueField [invalid_type]and…labelField [invalid_type]. - Edit the dashboard Description, then its Label. Each PUT carries the same incomplete item and is refused 422.
- Reload. No leave prompt (
new Event('beforeunload',{cancelable:true})leavesdefaultPreventedfalse). 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
objectstack-fleet commentedon Nov 2, 2026
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.
Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
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 sessionsession_01QsR2jRFuJz9QBZqPVHYVhJAccount:
zhuangjianguoBranch:
claude/project-thread-87tl4w(platform-assigned to the card thread; it can push only this branch)Worktree:
objectstack-issue-29006Domain:
domain:uiSeat:
domain:ui#2File surface:
ui/packages/app-shell/src/views/studio-design/metadataError.ts(listItemHeldInputand its doc comment only), its tests, and a root.changeset; notStudioDesignSurface.tsx(stop on breach; explain in the report)Container & model: S,
mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate),effort: defaultClause-②: 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 authorThread-read: none
Premise: holds (card measured at
7fa9873f; atorigin/main45e9b2f1,listItemHeldInput(~L1130) holds an item only when its unset input sits directly below the item; blocker PR #29143 landed 09:58Z as1e4841a2; no open PR touchesmetadataError.ts;GlobalFilterSchema.optionsFromunchanged, fix is client-side)Serial constraints cleared:
StudioDesignSurface.tsxis held by #28984 (seat 2), #29113 and #29148 (seat 5) and PR #29224 (seat 3), and is outside this surfaceobjectstack-fleet commentedon Nov 2, 2026
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" ] }objectstack-fleet commentedon Nov 2, 2026
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 (thelistItemHeldInputwalk below the item inmetadataError.ts, its test, the changeset). Delivered in full: nothing remains on this card;pm:dispatchedremoved.