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
A field authored in Studio as "Checkbox — shown as a checkbox" renders as a toggle switch on the Studio canvas and in the runtime create form #27519
Found in the 18.3.0 release-candidate browser walkthrough (wave anchor #27377, card W3-UX), at framework main @ c4c515ad, Console built in-repo from the same commit.
Repro (fresh boot)
Boot examples/app-showcase with --seed-admin and sign in as admin. Viewport 1440×900.
From Home choose New in Studio, create a package (e.g. "Field Service"), and create an object Work Order with an optional Number field Estimated Hours and a Picklist Status (option value new, label New Request). Create a second object, Site.
On Work Order, choose Add field. The type picker offers both "Checkbox — shown as a checkbox" and "Toggle — shown as a switch". Pick Checkbox and name it Urgent.
Look at the field on the Form canvas. Publish, then open the app in the Console and click New.
Expected
A checkbox, as the type picker promises. Only a field authored as Toggle draws a switch.
Actual
Urgent draws as a switch on the Studio canvas and in the runtime create dialog. On the record detail page, the unchecked value is an empty circle.
Read fresh. A field authored as type boolean, which the picker labels Checkbox, draws as a switch on the Studio canvas and in the runtime form, so the spec's distinct boolean and toggle types look the same. The authored choice is silently dropped: p2.
Use one type-to-widget map, shared by the canvas and the runtime form: boolean draws a checkbox and toggle draws a switch. No per-surface override.
Claim: PM loop round 1 · 2026-10-29T14:19Z
Session: session_01Utp6CY2n1JmZZKrHgtp9jC (seat; implementation by a card thread, mode:thread)
Account: os-bill
Branch: claude/project-thread-6ybbdj (the card thread's harness push branch, self-reported — the measured platform exception to claude/issue-N-slug)
Worktree: objectstack-issue-27519
Domain: domain:ui
Seat: domain:ui#2
File surface: ui/packages/fields/src/** (alias map, BooleanField, FieldEditWidget, cell renderer), ui/packages/components/src/renderers/form/form.tsx (boolean widget resolution only), ui/packages/app-shell/src/views/studio-design/ObjectFormDesigner.tsx (FieldControlPreview only, agreed with seat 3), tests beside, a changeset (stop on breach; explain in the report)
Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate), effort: high
Clause-②: yes (widening) — corrected by the seat from the diff: new exports resolveBooleanControl and type BooleanControl on @object-ui/fields; owes a TIER_DEFAULT declaration review at the final head. Accepted deviations: one import line in ObjectFormDesigner.tsx beside FieldControlPreview; four existing tests elsewhere (console ×2, plugin-detail, plugin-kanban) updated from switch to checkbox for boolean rows
Metadata-protocol: none (expected; the dev measures from the diff)
Responsibility: ui/packages/fields/src/field-type-alias.ts + widgets/BooleanField.tsx (both types collapse to one widget) and the Studio canvas preview | none | every author who picks Checkbox, and every user of such a form, today
Thread-read: 6495088373
Premise: corrected — on origin/mainee9a8d6e there is no shared type-to-widget map: the runtime collapses boolean and toggle into field:boolean and the canvas has its own switch; the fix builds the one resolution triage names
Serial constraints cleared: form.tsx is shared with this seat's #27247 (submit bar) and #27483 (field validation): different regions, ordinary concurrency, later lander merges main. ObjectFormDesigner.tsx: seat 3 confirmed none of its claims declare it; its #27511/#27485 thread (claude/project-thread-ufaqzz) stays out of FieldControlPreview.
Dispatch order: /mnt/project-files/ui-seat2/dispatch-27519.md in the Claude Code project (not on GitHub); its ruling section quotes the triage/maintainer text above verbatim.
{
"issue": 27519,
"status": "done",
"branch": "claude/project-thread-6ybbdj",
"pr": "https://github.com/objectstack-ai/objectstack/pull/27665",
"head": "302bcb25deb838acee2d21a1944b91923222e19c",
"session": "session_01Utp6CYXVQFhXabW3ZQcxWt (card thread; seat session_01Utp6CY2n1JmZZKrHgtp9jC)",
"premise_still_valid": true,
"summary": "New resolveBooleanControl (ui/packages/fields/src/widgets/booleanControl.ts, exported from @object-ui/fields) is the one type-to-widget map: an explicit boolean widget (checkbox/boolean -> checkbox, switch/toggle -> switch) wins, else type (boolean -> checkbox, toggle -> switch), else checkbox. BooleanField (record form field:boolean, and FieldEditWidget inline edit) and the Studio canvas FieldControlPreview both read it; form.tsx and the alias map needed no behaviour change. Six existing tests updated to the new faces, none deleted; field-type-coverage unchanged and green.",
"tests": "Ten boolean files: Test Files 10 passed (10), Tests 227 passed (227). Pre-merge wide run (fields, plugin-detail, plugin-kanban, plugin-form, components form renderer): Test Files 989 passed | 2 skipped (991), Tests 10835 passed | 43 skipped. Post-merge head 302bcb25 (closure rebuilt): components form renderer + plugin-form + app-shell studio-design + ten boolean files: Test Files 555 passed (555), Tests 4955 passed | 18 skipped. type-check fields/app-shell/plugin-detail/plugin-kanban exit 0. eslint on 13 changed ui files 0 errors. Ablation via scripts/ablation-replace.mjs (resolution return -> old widget==='checkbox' ? checkbox : switch; anchor 1->0): 3 files red, 13 of 28 cases incl. the canvas case; restored, blob == HEAD 5ea9e94e, git diff HEAD empty.",
"gates": "dispatch-gates --commands on 302bcb25: 34 families, all exit 0, recorded in /mnt/project-files/ui-seat2/27519-gates-ran.txt (project folder) — incl. node scripts/check-sdui-manifest.mjs :: exit 0, pnpm check:type-check-debt :: exit 0 (under the verify lock; the pre-merge run exited 3 queue-timeout behind my own test run and was re-run), pnpm --dir ui exec vitest run scripts/ :: exit 0. --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. pnpm check:new-line-citations: 0 new. CI check-runs: in_progress at report time.",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"line_budget": "14 files, +308 / -54 vs origin/main",
"files_changed": [
".changeset/27519-boolean-checkbox-toggle-switch.md",
"ui/packages/fields/src/widgets/booleanControl.ts",
"ui/packages/fields/src/widgets/BooleanField.tsx",
"ui/packages/fields/src/field-type-alias.ts",
"ui/packages/fields/src/index.tsx",
"ui/packages/app-shell/src/views/studio-design/ObjectFormDesigner.tsx",
"tests: fields booleanControl-27519, boolean-label-association-e2e, standard-widgets; app-shell ObjectFormDesigner.booleanControl-27519; console formPageRequiredMarker-10178, FormPage.sharedWidgets-10179; plugin-detail InlineFieldInput.delegation; plugin-kanban RequiredFieldsDialog.ariaInvalidText-7126"
],
"deviations": [
"ObjectFormDesigner.tsx: one import line added at the file head besides the FieldControlPreview body (needed to read the shared resolution); no other line outside the function.",
"Test files outside the declared surface updated (apps/console two, plugin-detail one, plugin-kanban one): they asserted a switch for a boolean row, updated to the ruled face, not deleted.",
"PR attribution link points at this card thread (session-context instruction), not the seat thread the dispatch order named.",
"No assignee and no reviewer on the PR (maintainer rule); the harness post-PR assign/review step skipped."
],
"mcp_calls": "3 — mcp__github__issue_read x3 (get, get_comments x2); no write tools",
"api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft, read back identical 8977 bytes), post-stamped issue comment on #27519 (this report); plus git pushes (not REST)",
"open_questions": [],
"out_of_scope_findings": [
"observation, not filed · record detail 'empty circle' for an unchecked boolean: not the same collapse and not reproduced in a browser; an editable boolean row draws BooleanCellRenderer's disabled square checkbox (rounded-sm, opacity-50), a non-boolean draws the em-dash EmptyValue, and the only round boolean face is the name-gated completion indicator · carrier: seat",
"observation, not filed · BooleanCellRenderer draws a disabled checkbox for both boolean and toggle, so a toggle cell reads as a checkbox in the grid and on an editable detail row; outside the ruling's canvas + runtime form · carrier: seat",
"observation, not filed · the canvas previews by type only: SortableField passes FieldControlPreview type={type}, so an authored widget override is not shown on the canvas without that one caller line (seat 3's area) · carrier: seat"
]
}
Activity
objectstack-fleet commentedon Oct 29, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: 能用的表单 | none | P②
Triage: p2 ·
domain:ui·area:records· 2026-10-29T11:11Z⛔ Not a claim.
bug,pm:queue.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC
Claim: PM loop round 1 · 2026-10-29T14:19Z
Session:
session_01Utp6CY2n1JmZZKrHgtp9jC(seat; implementation by a card thread,mode:thread)Account:
os-billBranch:
claude/project-thread-6ybbdj(the card thread's harness push branch, self-reported — the measured platform exception toclaude/issue-N-slug)Worktree:
objectstack-issue-27519Domain:
domain:uiSeat:
domain:ui#2File surface:
ui/packages/fields/src/**(alias map,BooleanField,FieldEditWidget, cell renderer),ui/packages/components/src/renderers/form/form.tsx(boolean widget resolution only),ui/packages/app-shell/src/views/studio-design/ObjectFormDesigner.tsx(FieldControlPreviewonly, agreed with seat 3), tests beside, a changeset (stop on breach; explain in the report)Container & model:
M,mode:thread,model: TIER_DEFAULT(dispatch-gates --tier: no path-derived mandate),effort: highClause-②: yes (widening) — corrected by the seat from the diff: new exports
resolveBooleanControlandtype BooleanControlon@object-ui/fields; owes a TIER_DEFAULT declaration review at the final head. Accepted deviations: one import line inObjectFormDesigner.tsxbesideFieldControlPreview; four existing tests elsewhere (console ×2, plugin-detail, plugin-kanban) updated from switch to checkbox for boolean rowsMetadata-protocol: none (expected; the dev measures from the diff)
Responsibility:
ui/packages/fields/src/field-type-alias.ts+widgets/BooleanField.tsx(both types collapse to one widget) and the Studio canvas preview | none | every author who picks Checkbox, and every user of such a form, todayThread-read: 6495088373
Premise: corrected — on
origin/mainee9a8d6ethere is no shared type-to-widget map: the runtime collapsesbooleanandtoggleintofield:booleanand the canvas has its own switch; the fix builds the one resolution triage namesSerial constraints cleared:
form.tsxis shared with this seat's #27247 (submit bar) and #27483 (field validation): different regions, ordinary concurrency, later lander mergesmain.ObjectFormDesigner.tsx: seat 3 confirmed none of its claims declare it; its #27511/#27485 thread (claude/project-thread-ufaqzz) stays out ofFieldControlPreview.Dispatch order:
/mnt/project-files/ui-seat2/dispatch-27519.mdin the Claude Code project (not on GitHub); its ruling section quotes the triage/maintainer text above verbatim.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC
os-dev-report
{ "issue": 27519, "status": "done", "branch": "claude/project-thread-6ybbdj", "pr": "https://github.com/objectstack-ai/objectstack/pull/27665", "head": "302bcb25deb838acee2d21a1944b91923222e19c", "session": "session_01Utp6CYXVQFhXabW3ZQcxWt (card thread; seat session_01Utp6CY2n1JmZZKrHgtp9jC)", "premise_still_valid": true, "summary": "New resolveBooleanControl (ui/packages/fields/src/widgets/booleanControl.ts, exported from @object-ui/fields) is the one type-to-widget map: an explicit boolean widget (checkbox/boolean -> checkbox, switch/toggle -> switch) wins, else type (boolean -> checkbox, toggle -> switch), else checkbox. BooleanField (record form field:boolean, and FieldEditWidget inline edit) and the Studio canvas FieldControlPreview both read it; form.tsx and the alias map needed no behaviour change. Six existing tests updated to the new faces, none deleted; field-type-coverage unchanged and green.", "tests": "Ten boolean files: Test Files 10 passed (10), Tests 227 passed (227). Pre-merge wide run (fields, plugin-detail, plugin-kanban, plugin-form, components form renderer): Test Files 989 passed | 2 skipped (991), Tests 10835 passed | 43 skipped. Post-merge head 302bcb25 (closure rebuilt): components form renderer + plugin-form + app-shell studio-design + ten boolean files: Test Files 555 passed (555), Tests 4955 passed | 18 skipped. type-check fields/app-shell/plugin-detail/plugin-kanban exit 0. eslint on 13 changed ui files 0 errors. Ablation via scripts/ablation-replace.mjs (resolution return -> old widget==='checkbox' ? checkbox : switch; anchor 1->0): 3 files red, 13 of 28 cases incl. the canvas case; restored, blob == HEAD 5ea9e94e, git diff HEAD empty.", "gates": "dispatch-gates --commands on 302bcb25: 34 families, all exit 0, recorded in /mnt/project-files/ui-seat2/27519-gates-ran.txt (project folder) — incl. node scripts/check-sdui-manifest.mjs :: exit 0, pnpm check:type-check-debt :: exit 0 (under the verify lock; the pre-merge run exited 3 queue-timeout behind my own test run and was re-run), pnpm --dir ui exec vitest run scripts/ :: exit 0. --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. pnpm check:new-line-citations: 0 new. CI check-runs: in_progress at report time.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "line_budget": "14 files, +308 / -54 vs origin/main", "files_changed": [ ".changeset/27519-boolean-checkbox-toggle-switch.md", "ui/packages/fields/src/widgets/booleanControl.ts", "ui/packages/fields/src/widgets/BooleanField.tsx", "ui/packages/fields/src/field-type-alias.ts", "ui/packages/fields/src/index.tsx", "ui/packages/app-shell/src/views/studio-design/ObjectFormDesigner.tsx", "tests: fields booleanControl-27519, boolean-label-association-e2e, standard-widgets; app-shell ObjectFormDesigner.booleanControl-27519; console formPageRequiredMarker-10178, FormPage.sharedWidgets-10179; plugin-detail InlineFieldInput.delegation; plugin-kanban RequiredFieldsDialog.ariaInvalidText-7126" ], "deviations": [ "ObjectFormDesigner.tsx: one import line added at the file head besides the FieldControlPreview body (needed to read the shared resolution); no other line outside the function.", "Test files outside the declared surface updated (apps/console two, plugin-detail one, plugin-kanban one): they asserted a switch for a boolean row, updated to the ruled face, not deleted.", "PR attribution link points at this card thread (session-context instruction), not the seat thread the dispatch order named.", "No assignee and no reviewer on the PR (maintainer rule); the harness post-PR assign/review step skipped." ], "mcp_calls": "3 — mcp__github__issue_read x3 (get, get_comments x2); no write tools", "api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft, read back identical 8977 bytes), post-stamped issue comment on #27519 (this report); plus git pushes (not REST)", "open_questions": [], "out_of_scope_findings": [ "observation, not filed · record detail 'empty circle' for an unchecked boolean: not the same collapse and not reproduced in a browser; an editable boolean row draws BooleanCellRenderer's disabled square checkbox (rounded-sm, opacity-50), a non-boolean draws the em-dash EmptyValue, and the only round boolean face is the name-gated completion indicator · carrier: seat", "observation, not filed · BooleanCellRenderer draws a disabled checkbox for both boolean and toggle, so a toggle cell reads as a checkbox in the grid and on an editable detail row; outside the ruling's canvas + runtime form · carrier: seat", "observation, not filed · the canvas previews by type only: SortableField passes FieldControlPreview type={type}, so an authored widget override is not shown on the canvas without that one caller line (seat 3's area) · carrier: seat" ] }objectstack-fleet commentedon Oct 29, 2026
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC
ACCEPT → PR #27665 comment 6503328013 (head
f2a547f9); contract review (declaration, TIER_DEFAULT) PASS 6502998669; landing through the merge queue.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC
Landed · 2026-10-29T18:17Z
50d05cb1cb9795df412dce0ae18f21386ebf9579completedby the PR'sFixeslineState:
pm:dispatchedremoved; the assignee stays as the record of who carried it.Generated by Claude Code