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: "Add item" (Order, joined blocks) and a chart type picked before its axes autosave an incomplete report (422), so later edits are refused; hold them as the dashboard lists are held after #27459 #27785
Filing gate: ① product defect, measured by QA run #27441 and recorded as step 7 of #27459. It is split out here because PR #27784 (Fixes #27459) covers only the dashboard path, which is the one named by that card's title, reproduction and QA clause.
Business reason: a report author who adds a sort row, a joined block or picks a chart type first sees the red "Changes not saved: the server refused this draft" banner. Every later edit to the report is then refused too and lost on reload. The Interfaces pillar holds such edits elsewhere.
Reader who acts: domain:ui (Studio, ui/packages/app-shell/src/views/studio-design/).
Duplicate search: search_issues "report Properties Add item 422 order.0.by held incomplete chart xAxis yAxis" → 1 hit (#3752, unrelated chart props). "Studio report autosave empty item refused 422 held until filled" → #27459 (the dashboard half, this card's parent), #26705 and #26845 (departure guards). None of them covers this.
On the report Properties panel in the Studio Interfaces pillar:
Order → "Add item": the request is PUT …?mode=draft with order:[{}], and the server answers 422 order.0.by.
joined report → "Add item": blocks:[{}] gets a 422.
Chart type "bar" picked before the axes: 422 chart.xAxis / chart.yAxis.
Each one shows the refusal banner instead of holding the edit. The next autosave sends the same body and is refused again.
Seam
spec:ReportSchema → renderer StudioDesignSurfaceInterfacesPillarleafIncomplete. The gate today is leafType === 'dashboard'. PR #27784 adds listItemHeldInput(body, schema, skip) and heldListItem(...) in metadataError.ts. These take any spec schema, so the two list cases (Order and joined blocks) should be able to reuse them against ReportSchema, with the report form for the labels.
The chart case is not a list item. It is a nested object (chart) refused only for its unset xAxis/yAxis. Either generalise the "every issue lands on an unset input" rule to that object, or hold it separately. The implementer decides and says which in the PR.
A report the spec refuses only because a just-added item, or the chart, has unset inputs is held and not sent. A neutral line names the input ("By on item Add metamodel interfaces for ObjectQL/ObjectUI contract #1 of “Order” needs a value"). Show me opens the report's properties at that list, and Publish refuses while the edit is held.
Once the input is filled, the next autosave sends every edit kept in the meantime.
An item refused for a value it already holds is sent, and its refusal shows as before.
⛔ No client-side list of required inputs: the judge is ReportSchema itself.
Acceptance
A measured repro of each of the three cases on current main first. A case that no longer reproduces is named and dropped.
Unit pins against the real ReportSchema (held / not held / finished-but-wrong), the same shape as metadataError.heldListItem-27459.test.ts.
Claim: PM loop round 1
Session: session_01Utp6CaJoinvGQSbHeDaWBe
Account: os-bill
Branch: claude/project-thread-h27bhe (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-27785
Domain: domain:ui
Seat: domain:ui#3
File surface: ui/packages/app-shell/src/views/studio-design/metadataError.ts (report held rule beside the dashboard one), StudioDesignSurface.tsx InterfacesPillar leafIncomplete / held-line wiring, studio i18n en/zh held-clause key only if listItemNeedsInput does not fit the chart case, tests beside, root changeset (stop on breach; explain in the report)
Container & model: S/M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier on 178b5238: no path-derived mandate), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: ui code produces the defect | no platform path covers it | every Studio / console author reaches it today
Thread-read: none
Premise: holds
Serial constraints cleared: builds on PR #27784 (#27459, in merge queue): merge origin/main after it lands, or build on its head 7ff9cb5 and say so
Dispatched 2026-10-29T23:27Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.
{
"issue": 27785,
"status": "done",
"branch": "claude/project-thread-h27bhe",
"pr": "https://github.com/objectstack-ai/objectstack/pull/27824",
"head": "93747963ab373f1193e9fde274532622053e2a39",
"session": "session_01Utp6CaQABe2o9G1TyQ3LTx (card thread; seat session_01Utp6CaJoinvGQSbHeDaWBe)",
"premise_still_valid": true,
"summary": "All three cases measured on ReportSchema (spec dist of this tree): order:[{}] -> order.0.by; blocks:[{}] -> blocks.0.name (then blocks.0.dataset once named); chart:{type:'bar'} -> chart.xAxis, chart.yAxis. The pillar gate was leafType === 'dashboard', so the report body was sent (ablation shows the order:[{}] PUT). New reportHeldEdit (metadataError.ts) asks listItemHeldInput(draft, ReportSchema) with getReportForm() labels (#27784's rule, unchanged), else the chart through a new objectHeldInput(body, schema, 'chart'): held only when every issue under chart names an input directly below it that the chart leaves unset; a chart refused for a value it holds is sent. The chart is held on its own rather than generalising listItemHeldInput to every nested object (that would change the dashboard's holds unmeasured, and the chart's inputs are named by the inspector's chart panel labels, not spec form rows). New clause engine.studio.held.reportChartNeedsInput (en, zh). InterfacesPillar leafIncomplete asks reportHeldEdit for a report leaf; target null so Show me goes through showLeafRefusal. Built on #27784 head 7ff9cb5c, merged origin/main after #27784 landed (23:56Z); diff vs main is the 7 files below.",
"tests": "New metadataError.reportHeld-27785 (16) + InterfacesPillar.reportHeld-27785 (7, real ReportDefaultInspector): 23/23 passed. Affected suites (30 app-shell files naming metadataError/reportHeldEdit/dashboardHeldEdit/StudioHeld/ReportDefaultInspector/held.listItem + packages/i18n/): Test Files 120 passed, Tests 1631 passed | 13 skipped, lock VERDICT 0, on dae67768 and again on 93747963. Ablations (ablation-replace, app-shell imports src, no dist leg): (1) StudioDesignSurface.tsx report branch deleted -> 5 failed / 7 pillar cases (all hold cases; the order:[{}] body is sent), restored blob 064578278d42 == HEAD, git diff HEAD empty; (2) objectHeldInput's itemInputUnset guard removed -> 1 failed / 16 (chart refused for a held value), restored blob 90e17158edd5 == HEAD, git diff HEAD empty.",
"gates": [
"node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0",
"node scripts/check-adr-0087-registration.mjs --self-test :: exit 0",
"node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0",
"node scripts/check-closing-keyword-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
"node scripts/check-comment-mask-corpus.mjs :: exit 0",
"node scripts/check-empty-changeset.mjs --base origin/main :: exit 0",
"node scripts/check-empty-changeset.mjs --self-test :: exit 0",
"node scripts/check-sdui-manifest.mjs :: exit 0",
"node scripts/check-sdui-manifest.mjs --self-test :: exit 0",
"node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0",
"node scripts/release-pending-publish.mjs --self-test :: exit 0",
"pnpm --dir ui exec vitest run scripts/ :: exit 0",
"pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0",
"pnpm check:changeset-gate-self-tests :: exit 0",
"pnpm check:closed-ceremony-text :: exit 0",
"pnpm check:cross-package-test-inputs :: exit 0",
"pnpm check:dispatcher-error-vocabulary :: exit 0",
"pnpm check:driver-memory-census :: exit 0",
"pnpm check:engine-double-contract :: exit 0",
"pnpm check:gitlink-declared :: exit 0",
"pnpm check:nul-bytes :: exit 0",
"pnpm check:pm-changeset-deadline-census :: exit 0",
"pnpm check:published-files :: exit 0",
"pnpm check:query-options-erasure :: exit 0",
"pnpm check:refd-timer-probe :: exit 0",
"pnpm check:skill-identifier-liveness :: exit 0",
"pnpm check:test-source-alias :: exit 0",
"pnpm check:tier-file-adoption :: exit 0",
"pnpm check:type-check-coverage :: exit 0",
"pnpm check:type-check-debt :: exit 0",
"pnpm check:undeclared-dep-imports :: exit 0",
"pnpm check:watch-hint-literal :: exit 0",
"pnpm check:where-matcher :: exit 0",
"dispatch-gates --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN (head 93747963; the same 34 also 34/34 exit 0 on pre-merge dae67768)",
"ui check-new-cross-file-line-citations :: exit 0 (0 new)",
"pnpm --filter @object-ui/app-shell run type-check && pnpm --filter @object-ui/i18n run type-check :: exit 0 (head 93747963)",
"PR check-runs: in_progress at report time"
],
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"line_budget": "7 files, +576 / -6 vs main (tests +466 of that)",
"files_changed": [
".changeset/studio-report-held-edit.md",
"ui/packages/app-shell/src/views/studio-design/InterfacesPillar.reportHeld-27785.test.tsx",
"ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx",
"ui/packages/app-shell/src/views/studio-design/metadataError.reportHeld-27785.test.ts",
"ui/packages/app-shell/src/views/studio-design/metadataError.ts",
"ui/packages/i18n/src/namespaces/studio/en.ts",
"ui/packages/i18n/src/namespaces/studio/zh.ts"
],
"deviations": [
"Built on PR #27784 head 7ff9cb5c (in the merge queue at start), then merged origin/main after it landed; the merge conflicted only on adjacent lines (#27784 landed as a squash beside this branch's additions), resolved keeping both.",
"New i18n clause engine.studio.held.reportChartNeedsInput (en, zh): listItemNeedsInput names an item number and a list, which a chart does not have (the order allows a key in that case).",
"listItemHeldInput's inline schema type extracted to a local LeafSpecSchema alias, shared with objectHeldInput; no behaviour change.",
"Harness after-PR assignee/review step skipped per the order (no assignees, no review request); harness PR auto-subscription not used for waiting.",
"Commit trailers model-free per AGENTS.md (the harness reminder asked for a model-named Co-Authored-By)."
],
"mcp_calls": "0 — no GitHub MCP calls (claude-code-remote get_session only)",
"api_writes": "2 — relay pr_create (repository_dispatch, PR #27824); this report comment via post-stamped (POST issues/27785/comments)",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed: a joined report with chart:{type} set is held for the axes (ReportSchema judges a joined container's chart only once it parses); the inspector hides the chart panel on joined and clears chart on the switch, so the Studio cannot produce it (PR Acceptance notes)",
"carrier: none · noted, not filed: an empty sort key inside a block's own order is not held (#27784's rule places it on the block, whose order holds a value); whether the block repeater can produce one was not measured (PR Acceptance notes)",
"carrier: seat · dashboard residual unchanged: Show me does not expand a collapsed SchemaForm section (dashboard Filters); the report rows this card holds are in uncollapsed sections"
]
}
Activity
objectstack-fleet commentedon Oct 29, 2026
domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe
Claim: PM loop round 1
Session:
session_01Utp6CaJoinvGQSbHeDaWBeAccount:
os-billBranch:
claude/project-thread-h27bhe(the card thread's designated push branch, the recorded platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-27785Domain:
domain:uiSeat:
domain:ui#3File surface: ui/packages/app-shell/src/views/studio-design/metadataError.ts (report held rule beside the dashboard one), StudioDesignSurface.tsx InterfacesPillar leafIncomplete / held-line wiring, studio i18n en/zh held-clause key only if listItemNeedsInput does not fit the chart case, tests beside, root changeset (stop on breach; explain in the report)
Container & model:
S/M,mode:thread,model: TIER_DEFAULT(dispatch-gates --tieron178b5238: no path-derived mandate),effort: highClause-②: no
Metadata-protocol: none
Responsibility: ui code produces the defect | no platform path covers it | every Studio / console author reaches it today
Thread-read: none
Premise: holds
Serial constraints cleared: builds on PR #27784 (#27459, in merge queue): merge origin/main after it lands, or build on its head 7ff9cb5 and say so
Dispatched 2026-10-29T23:27Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.
Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
domain:ui#3 · os-bill · session_01Utp6CaQABe2o9G1TyQ3LTx
os-dev-report
{ "issue": 27785, "status": "done", "branch": "claude/project-thread-h27bhe", "pr": "https://github.com/objectstack-ai/objectstack/pull/27824", "head": "93747963ab373f1193e9fde274532622053e2a39", "session": "session_01Utp6CaQABe2o9G1TyQ3LTx (card thread; seat session_01Utp6CaJoinvGQSbHeDaWBe)", "premise_still_valid": true, "summary": "All three cases measured on ReportSchema (spec dist of this tree): order:[{}] -> order.0.by; blocks:[{}] -> blocks.0.name (then blocks.0.dataset once named); chart:{type:'bar'} -> chart.xAxis, chart.yAxis. The pillar gate was leafType === 'dashboard', so the report body was sent (ablation shows the order:[{}] PUT). New reportHeldEdit (metadataError.ts) asks listItemHeldInput(draft, ReportSchema) with getReportForm() labels (#27784's rule, unchanged), else the chart through a new objectHeldInput(body, schema, 'chart'): held only when every issue under chart names an input directly below it that the chart leaves unset; a chart refused for a value it holds is sent. The chart is held on its own rather than generalising listItemHeldInput to every nested object (that would change the dashboard's holds unmeasured, and the chart's inputs are named by the inspector's chart panel labels, not spec form rows). New clause engine.studio.held.reportChartNeedsInput (en, zh). InterfacesPillar leafIncomplete asks reportHeldEdit for a report leaf; target null so Show me goes through showLeafRefusal. Built on #27784 head 7ff9cb5c, merged origin/main after #27784 landed (23:56Z); diff vs main is the 7 files below.", "tests": "New metadataError.reportHeld-27785 (16) + InterfacesPillar.reportHeld-27785 (7, real ReportDefaultInspector): 23/23 passed. Affected suites (30 app-shell files naming metadataError/reportHeldEdit/dashboardHeldEdit/StudioHeld/ReportDefaultInspector/held.listItem + packages/i18n/): Test Files 120 passed, Tests 1631 passed | 13 skipped, lock VERDICT 0, on dae67768 and again on 93747963. Ablations (ablation-replace, app-shell imports src, no dist leg): (1) StudioDesignSurface.tsx report branch deleted -> 5 failed / 7 pillar cases (all hold cases; the order:[{}] body is sent), restored blob 064578278d42 == HEAD, git diff HEAD empty; (2) objectHeldInput's itemInputUnset guard removed -> 1 failed / 16 (chart refused for a held value), restored blob 90e17158edd5 == HEAD, git diff HEAD empty.", "gates": [ "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0", "node scripts/check-adr-0087-registration.mjs --self-test :: exit 0", "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0", "node scripts/check-closing-keyword-parity.mjs :: exit 0", "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0", "node scripts/check-comment-mask-corpus.mjs :: exit 0", "node scripts/check-empty-changeset.mjs --base origin/main :: exit 0", "node scripts/check-empty-changeset.mjs --self-test :: exit 0", "node scripts/check-sdui-manifest.mjs :: exit 0", "node scripts/check-sdui-manifest.mjs --self-test :: exit 0", "node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0", "node scripts/release-pending-publish.mjs --self-test :: exit 0", "pnpm --dir ui exec vitest run scripts/ :: exit 0", "pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0", "pnpm check:changeset-gate-self-tests :: exit 0", "pnpm check:closed-ceremony-text :: exit 0", "pnpm check:cross-package-test-inputs :: exit 0", "pnpm check:dispatcher-error-vocabulary :: exit 0", "pnpm check:driver-memory-census :: exit 0", "pnpm check:engine-double-contract :: exit 0", "pnpm check:gitlink-declared :: exit 0", "pnpm check:nul-bytes :: exit 0", "pnpm check:pm-changeset-deadline-census :: exit 0", "pnpm check:published-files :: exit 0", "pnpm check:query-options-erasure :: exit 0", "pnpm check:refd-timer-probe :: exit 0", "pnpm check:skill-identifier-liveness :: exit 0", "pnpm check:test-source-alias :: exit 0", "pnpm check:tier-file-adoption :: exit 0", "pnpm check:type-check-coverage :: exit 0", "pnpm check:type-check-debt :: exit 0", "pnpm check:undeclared-dep-imports :: exit 0", "pnpm check:watch-hint-literal :: exit 0", "pnpm check:where-matcher :: exit 0", "dispatch-gates --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN (head 93747963; the same 34 also 34/34 exit 0 on pre-merge dae67768)", "ui check-new-cross-file-line-citations :: exit 0 (0 new)", "pnpm --filter @object-ui/app-shell run type-check && pnpm --filter @object-ui/i18n run type-check :: exit 0 (head 93747963)", "PR check-runs: in_progress at report time" ], "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "line_budget": "7 files, +576 / -6 vs main (tests +466 of that)", "files_changed": [ ".changeset/studio-report-held-edit.md", "ui/packages/app-shell/src/views/studio-design/InterfacesPillar.reportHeld-27785.test.tsx", "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx", "ui/packages/app-shell/src/views/studio-design/metadataError.reportHeld-27785.test.ts", "ui/packages/app-shell/src/views/studio-design/metadataError.ts", "ui/packages/i18n/src/namespaces/studio/en.ts", "ui/packages/i18n/src/namespaces/studio/zh.ts" ], "deviations": [ "Built on PR #27784 head 7ff9cb5c (in the merge queue at start), then merged origin/main after it landed; the merge conflicted only on adjacent lines (#27784 landed as a squash beside this branch's additions), resolved keeping both.", "New i18n clause engine.studio.held.reportChartNeedsInput (en, zh): listItemNeedsInput names an item number and a list, which a chart does not have (the order allows a key in that case).", "listItemHeldInput's inline schema type extracted to a local LeafSpecSchema alias, shared with objectHeldInput; no behaviour change.", "Harness after-PR assignee/review step skipped per the order (no assignees, no review request); harness PR auto-subscription not used for waiting.", "Commit trailers model-free per AGENTS.md (the harness reminder asked for a model-named Co-Authored-By)." ], "mcp_calls": "0 — no GitHub MCP calls (claude-code-remote get_session only)", "api_writes": "2 — relay pr_create (repository_dispatch, PR #27824); this report comment via post-stamped (POST issues/27785/comments)", "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed: a joined report with chart:{type} set is held for the axes (ReportSchema judges a joined container's chart only once it parses); the inspector hides the chart panel on joined and clears chart on the switch, so the Studio cannot produce it (PR Acceptance notes)", "carrier: none · noted, not filed: an empty sort key inside a block's own order is not held (#27784's rule places it on the block, whose order holds a value); whether the block repeater can produce one was not measured (PR Acceptance notes)", "carrier: seat · dashboard residual unchanged: Show me does not expand a collapsed SchemaForm section (dashboard Filters); the report rows this card holds are in uncollapsed sections" ] }Report taken 2026-10-30T00:56Z.
objectstack-fleet commentedon Oct 30, 2026
domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe
ACCEPT → PR #27824 comment 6512481010 · 2026-10-30T01:14Z
Generated by Claude Code