You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Studio report Properties: switching Report type to Joined autosaves a report with no blocks (422), and later edits are refused and lost on reload with no prompt #29022
Filing gate: ① a product defect, found beside the clauses (studio-authoring.report-authoring-options), reach: the Studio autosave, measured 3 times.
Business reason: a report author who switches a report to Joined and touches anything before adding the first block sees "Changes not saved", and every edit is then refused and lost on reload without a prompt. #27785 holds an added Order or Blocks item and a chart type picked before its axes; the type switch itself is the remaining door into the same loss.
Reader who acts: the triage seat, then a domain:ui implementer in ui/packages/app-shell/src/views/studio-design/metadataError.ts. QA suggestion only: area:studio, p2.
Duplicate search: joined report Studio blocks → 65 hits; #27785 (closed) is the sibling for list items and chart axes, #21702 (closed) the spec side of a joined report with no dataset; none is the type switch.
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
/_console/studio/PKG/interfaces → nav "+" → Report → a name, a dataset and a measure → Save as draft (PUT 200, type: summary).
In Properties set Report type = Joined. Studio autosaves {"type":"joined","drilldown":true,…} with dataset and values dropped and no blocks → 422 at blocks, "a joined report needs blocks".
Edit Description → the PUT carries the same body plus the description → 422.
Reload: no beforeunload guard (defaultPrevented false) and no prompt; the report is back to Summary and the description is gone.
Expected: the switch is held until a block is complete, or the refused edits are guarded on leave. Actual: as above. Reproduced 3 times on fresh loads.
Mechanism (suspected)
reportHeldEdit in metadataError.ts does not treat a joined report with no blocks as held.
QA-source: #29002 · studio-authoring.report-authoring-options · observed beside the clauses
Read fresh. reportHeldEdit in ui/packages/app-shell/src/views/studio-design/metadataError.ts does not hold a joined report with no blocks, so Studio autosaves a body that gets a 422 and later edits are lost on reload; domain:ui. Silent loss of an author's edits is a user-visible defect, p2. It has a named location and a 3x repro, so pm:queue.
Claim: PM loop round 19 · 2026-11-02T07:35Z
Session: session_01QsR2jUqfeyd3MJX6eCeZdA (card thread, os-dev); dispatched by seat session session_01QsR2jUJ19MstFvFhjgiYTu
Account: zhuangjianguo
Branch: claude/project-thread-wbw17z (platform-assigned to the card thread; it can push only this branch)
Worktree: objectstack-issue-29022
Domain: domain:ui
Seat: domain:ui#1
File surface: ui/packages/app-shell/src/views/studio-design/metadataError.ts and the Studio report properties editor the dev names in a claim amendment before code, their tests, and a root .changeset (stop on breach; explain in the report)
Container & model: M, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: medium
Clause-②: no
Metadata-protocol: none
Responsibility: switching a Studio report to Joined autosaves a report with no blocks (422) and later edits are lost
Thread-read: 6571473866
Premise: holds (at origin/main5d3667e2: the card measures the 422 on the Joined switch; the dev re-measures before code)
Serial constraints cleared: #29006 waits for this card (both edit metadataError.ts).
Claim amended · 2026-11-02T07:36Z · after the dev's reading at origin/main5d3667e2. This sets the file surface and adds to the round 19 claim on this card. Branch, session and size are unchanged.
Measured (source reading): the report properties editor is ui/packages/app-shell/src/views/metadata-admin/inspectors/ReportDefaultInspector.tsx. Its commitType sends type: joined and clears dataset, values, rows, columns, chart and order (objectui#10746), leaving no blocks; ReportSchema refuses that at blocks. reportHeldEdit in metadataError.ts holds only list items and the chart, so the leaf autosave sends the invalid report (422). Separately, InterfacesPillar reports only navDirty through onDirtyChange, so a leaf edit refused with a 422 is seen by no guard, and a reload drops it silently.
File surface (set):ui/packages/app-shell/src/views/studio-design/metadataError.ts (reportHeldEdit gains one arm: a joined report with missing or empty blocks is held, its clause naming the form's Blocks list, with Show me pointing at it), a new test beside it, one new key engine.studio.held.reportJoinedNeedsBlock in ui/packages/i18n/src/namespaces/studio/ (en.ts, zh.ts), and a root .changeset. ReportDefaultInspector.tsx needs no change.
{
"issue": 29022,
"status": "done",
"branch": "claude/project-thread-wbw17z",
"pr": "https://github.com/objectstack-ai/objectstack/pull/29143",
"session": "session_01QsR2jUqfeyd3MJX6eCeZdA",
"premise_still_valid": true,
"summary": "reportHeldEdit (metadataError.ts) gains one arm, joinedReportHeld: a report whose type is joined and whose blocks are missing or empty is held unsent when ReportSchema's only refusal is at blocks, with a clause naming the report form's Blocks list and Show me focusing it; an added block is then judged by the existing list-item rule. A joined report with no blocks that also carries another refused key is still sent, so that refusal stays on screen. One new studio-namespace string (en, zh). The held clause already reaches the surface's beforeunload, header guard and Publish refusal, so the card's reload loss no longer happens on this path. ReportDefaultInspector.tsx unchanged. Within claim 6571686909 and amendment 6571700500; the second new test file (InterfacesPillar.reportJoined-29022.test.tsx) is a test of this arm through the real inspector, beside the 27785 pair.",
"tests": "Head 21198aa4. New: metadataError.reportHeld-29022.test.ts + InterfacesPillar.reportJoined-29022.test.tsx, 12/12 passed. Ablation (scripts/ablation-replace.mjs, delete `if (joined) return joined;`, anchor 1 to 0, blob f915fdc4 to dea5df87): 6 failed / 6 passed (the 4 held unit cases + pillar not-sent and Show me; controls and list-item handover stay green); restored, blob == HEAD, git diff HEAD empty. Sweep under the lock: 35 app-shell files naming reportHeldEdit / engine.studio.held / ReportDefaultInspector + zh-base-term.i18n-13131 + packages/i18n/: 131 files, 1701 passed, 13 skipped. turbo type-check app-shell + i18n: 66/66, VERDICT exit 0. Narrowing: whole app-shell vitest is CI's Test UI.",
"gates": "dispatch-gates --commands: 34 derived, all exit 0 (check-sdui-manifest, check-changeset-no-major, check-adr-0087-registration and check-closing-keyword-parity run against the final PR body, check-partof-closing-keyword exit 0 too; pnpm --dir ui exec vitest run scripts/ 169 files passed, 2 skipped). dispatch-gates --ran: 34 derived, 34 run, 0 NOT-MEASURED. PR check-runs at report time on 21198aa4: 17 in_progress, 3 skipped; mergeable_state blocked (draft, checks pending).",
"dogfood_battery": "n/a: no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "1: get_session (read). No MCP GitHub calls.",
"api_writes": "2: fleet-write pr_create (run 41411166546, PR #29143, read-back identical, no assignees, no labels); this os-dev-report comment via post-stamped. Plus git push of the branch.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: source reading only, not measured on a public door · ReportDefaultInspector.tsx JOINED_CONTAINER_CLEARED_KEYS clears dataset/values/rows/columns/chart/order on the Joined switch but not dateRange or exportOptions, which ReportSchema's joined arm also refuses (#26548) and whose Period and export section report.form.ts hides on a joined report (visibleWhen type != joined). A report with either key set, switched to Joined, is sent and refused at a key no visible control reaches · dedupe words: joined report dateRange exportOptions type switch cleared keys",
"class: a · reach: source reading (StudioDesignSurface.tsx InterfacesPillar onDirtyChange mirrors only navDirty; heldEdit carries only held clauses) · a leaf save refused with a 422 leaves later edits that no beforeunload, header or rail guard sees · carrier: seat (filing it as its own card, per the amendment) · dedupe words: refused save guard beforeunload leaf ifDirty Studio"
]
}
Fixes #29022 closed this card. pm:dispatched comes off in the same stroke as this note. #29006 (seat 2) waited on this PR and can now be ordered on origin/main.
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: studio-authoring.report-authoring-options
Triage:
domain:ui· p2 ·pm:queue· 2026-11-02T07:21Z⛔ Not a claim.
Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu
Claim: PM loop round 19 · 2026-11-02T07:35Z
Session:
session_01QsR2jUqfeyd3MJX6eCeZdA(card thread, os-dev); dispatched by seat sessionsession_01QsR2jUJ19MstFvFhjgiYTuAccount:
zhuangjianguoBranch:
claude/project-thread-wbw17z(platform-assigned to the card thread; it can push only this branch)Worktree:
objectstack-issue-29022Domain:
domain:uiSeat:
domain:ui#1File surface:
ui/packages/app-shell/src/views/studio-design/metadataError.tsand the Studio report properties editor the dev names in a claim amendment before code, their tests, and a root.changeset(stop on breach; explain in the report)Container & model: M,
mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate),effort: mediumClause-②: no
Metadata-protocol: none
Responsibility: switching a Studio report to Joined autosaves a report with no blocks (422) and later edits are lost
Thread-read: 6571473866
Premise: holds (at
origin/main5d3667e2: the card measures the 422 on the Joined switch; the dev re-measures before code)Serial constraints cleared: #29006 waits for this card (both edit
metadataError.ts).objectstack-fleet commentedon Nov 2, 2026
domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu
Claim amended · 2026-11-02T07:36Z · after the dev's reading at
origin/main5d3667e2. This sets the file surface and adds to the round 19 claim on this card. Branch, session and size are unchanged.ui/packages/app-shell/src/views/metadata-admin/inspectors/ReportDefaultInspector.tsx. ItscommitTypesendstype: joinedand clears dataset, values, rows, columns, chart and order (objectui#10746), leaving no blocks;ReportSchemarefuses that atblocks.reportHeldEditinmetadataError.tsholds only list items and the chart, so the leaf autosave sends the invalid report (422). Separately,InterfacesPillarreports onlynavDirtythroughonDirtyChange, so a leaf edit refused with a 422 is seen by no guard, and a reload drops it silently.ui/packages/app-shell/src/views/studio-design/metadataError.ts(reportHeldEditgains one arm: a joined report with missing or empty blocks is held, its clause naming the form's Blocks list, with Show me pointing at it), a new test beside it, one new keyengine.studio.held.reportJoinedNeedsBlockinui/packages/i18n/src/namespaces/studio/(en.ts,zh.ts), and a root.changeset.ReportDefaultInspector.tsxneeds no change.StudioDesignSurface.tsx(InterfacesPillar). That file is held by Studio: a dashboard draft whose requiredPermissions the author does not hold reads 403, Studio treats that as "no draft", and the next autosave overwrites the draft without the key #29015 right now and Studio: a draft autosave names nothing — only the top bar's "Saved HH:MM" shows, which the write-feedback convention does not count (ruling on #28345) #28984 and 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 queue behind it. With the Joined door closed, the card's measured loss no longer happens; the seat files the general guard gap as its own card.Record<string, string>and shipped under the separate./namespaces/studioexport, not the locale packs whose types list literal keys (the Studio: an All metadata area in the builder — directory, lists, generic item page, history and diagnostics, per package and across packages; pillar types open their pillar #28561 ruling). Metadata-protocol stays none.objectstack-fleet commentedon Nov 2, 2026
domain:ui#1 · zhuangjianguo · session_01QsR2jUqfeyd3MJX6eCeZdA
os-dev-report
{ "issue": 29022, "status": "done", "branch": "claude/project-thread-wbw17z", "pr": "https://github.com/objectstack-ai/objectstack/pull/29143", "session": "session_01QsR2jUqfeyd3MJX6eCeZdA", "premise_still_valid": true, "summary": "reportHeldEdit (metadataError.ts) gains one arm, joinedReportHeld: a report whose type is joined and whose blocks are missing or empty is held unsent when ReportSchema's only refusal is at blocks, with a clause naming the report form's Blocks list and Show me focusing it; an added block is then judged by the existing list-item rule. A joined report with no blocks that also carries another refused key is still sent, so that refusal stays on screen. One new studio-namespace string (en, zh). The held clause already reaches the surface's beforeunload, header guard and Publish refusal, so the card's reload loss no longer happens on this path. ReportDefaultInspector.tsx unchanged. Within claim 6571686909 and amendment 6571700500; the second new test file (InterfacesPillar.reportJoined-29022.test.tsx) is a test of this arm through the real inspector, beside the 27785 pair.", "tests": "Head 21198aa4. New: metadataError.reportHeld-29022.test.ts + InterfacesPillar.reportJoined-29022.test.tsx, 12/12 passed. Ablation (scripts/ablation-replace.mjs, delete `if (joined) return joined;`, anchor 1 to 0, blob f915fdc4 to dea5df87): 6 failed / 6 passed (the 4 held unit cases + pillar not-sent and Show me; controls and list-item handover stay green); restored, blob == HEAD, git diff HEAD empty. Sweep under the lock: 35 app-shell files naming reportHeldEdit / engine.studio.held / ReportDefaultInspector + zh-base-term.i18n-13131 + packages/i18n/: 131 files, 1701 passed, 13 skipped. turbo type-check app-shell + i18n: 66/66, VERDICT exit 0. Narrowing: whole app-shell vitest is CI's Test UI.", "gates": "dispatch-gates --commands: 34 derived, all exit 0 (check-sdui-manifest, check-changeset-no-major, check-adr-0087-registration and check-closing-keyword-parity run against the final PR body, check-partof-closing-keyword exit 0 too; pnpm --dir ui exec vitest run scripts/ 169 files passed, 2 skipped). dispatch-gates --ran: 34 derived, 34 run, 0 NOT-MEASURED. PR check-runs at report time on 21198aa4: 17 in_progress, 3 skipped; mergeable_state blocked (draft, checks pending).", "dogfood_battery": "n/a: no `!` in the PR title and no (narrowing) arm", "mcp_calls": "1: get_session (read). No MCP GitHub calls.", "api_writes": "2: fleet-write pr_create (run 41411166546, PR #29143, read-back identical, no assignees, no labels); this os-dev-report comment via post-stamped. Plus git push of the branch.", "open_questions": [], "out_of_scope_findings": [ "class: a · reach: source reading only, not measured on a public door · ReportDefaultInspector.tsx JOINED_CONTAINER_CLEARED_KEYS clears dataset/values/rows/columns/chart/order on the Joined switch but not dateRange or exportOptions, which ReportSchema's joined arm also refuses (#26548) and whose Period and export section report.form.ts hides on a joined report (visibleWhen type != joined). A report with either key set, switched to Joined, is sent and refused at a key no visible control reaches · dedupe words: joined report dateRange exportOptions type switch cleared keys", "class: a · reach: source reading (StudioDesignSurface.tsx InterfacesPillar onDirtyChange mirrors only navDirty; heldEdit carries only held clauses) · a leaf save refused with a 422 leaves later edits that no beforeunload, header or rail guard sees · carrier: seat (filing it as its own card, per the amendment) · dedupe words: refused save guard beforeunload leaf ifDirty Studio" ] }Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu
Landed · 2026-11-02T09:59Z · PR #29143 merged through the merge queue as
1e4841a2at 2026-11-02T09:58Z (GitHub's clock). ACCEPT 6572697572.1e4841a2~1)origin/main)joinedReportHeldinui/packages/app-shell/src/views/studio-design/metadataError.tsInterfacesPillar.reportJoined-29022.test.tsxexistsFixes #29022closed this card.pm:dispatchedcomes off in the same stroke as this note. #29006 (seat 2) waited on this PR and can now be ordered onorigin/main.