Repository navigation
fix(plugin-calendar,app-shell): quick-create hands an object it cannot satisfy to the full create form with the clicked day (objectui#12291) - #12315
Conversation
…ct to the full create form with the clicked day (objectui#12291) Before the quick-create dialog is offered, the calendar checks the object's required fields against what the dialog's write can fill, read from one statement of that set (`quickCreateFill`) that the write itself now uses. Where a required field is outside it (the stock showcase_task's required `project` lookup), the empty-day click and the week/day range drag call the declared `onEdit` prop with a new record carrying the clicked day instead of offering a dialog the server would refuse. With no `onEdit`, the gesture offers nothing on such an object. The console's record-form handler already read a record with no id as a new one; it now opens the create form with the values such a record carries: the overlay through the history entry that opens it, a page-mode object through the query-string prefill its create page reads. Claude-Session: https://claude.ai/code/session_01RZYixBnXPWvcm2YenWo6eJ Co-authored-by: Claude <noreply@anthropic.com>
… any (objectui#12291) Claude-Session: https://claude.ai/code/session_01RZYixBnXPWvcm2YenWo6eJ Co-authored-by: Claude <noreply@anthropic.com>
…y its title textbox (objectui#12291) Claude-Session: https://claude.ai/code/session_01RZYixBnXPWvcm2YenWo6eJ Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: Inputs: card objectui#12291 (body, triage 6137205429, claim 6139306452, os-dev-report 6140477085), PR objectui#12315 (body, the 10-file list, the net diff against ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: FAIL Generated by Claude Code |
…lendar-quick-create-handoff Claude-Session: https://claude.ai/code/session_01RZYixBnXPWvcm2YenWo6eJ Co-authored-by: Claude <noreply@anthropic.com>
… (objectui#12291) The published calendar newly calls its declared `onEdit` with an id-less record, and the console's record-form handler newly honours a seeded create: a Clause-② `yes` widening, so both packages take `minor`. Claude-Session: https://claude.ai/code/session_01RZYixBnXPWvcm2YenWo6eJ Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: Round 2, after the patch round for record 6140680761 (FAIL on ② alone). Inputs: card objectui#12291 (body and all four comments: triage 6137205429, claim 6139306452, os-dev-report 6140477085, patch-round os-dev-report 6140723169), PR objectui#12315 (body, the 10-file list, the net diff against What moved between the heads, measured. The compare is 6 commits ahead, 0 behind: the merge of ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #12291
Clause-②: yes
What changed
The calendar (
@object-ui/plugin-calendar,ObjectCalendar). Before the quick-create dialog is offered, the calendar checks the object's required fields against what the dialog's write can fill. That set used to be stated only inside the write's auto-fill loop. It is now one function,quickCreateFill, and both the write and the check (requiredFieldsQuickCreateCannotFill) call it, so the two cannot drift. A required field the server fills on insert does not count: one declaredreadonlyorsystem, or of typeautonumber. These are the exemptions objectql's insert walk (validateRecordInScope,isCallerWritableField) makes before its required check.crm_activity).showcase_task, requiredprojectlookup), the empty-day click and the week/day range drag no longer offer the dialog. They call the declaredonEditprop with a new record (noid) that carries the date values the dialog would have written, for example{ due_date: '2026-10-20' }. The host then opens its full create form pre-filled with them.onEdit, the gesture offers nothing on such an object. It is withheld the way a closedcalendarQuickCreategrant withholds it.onDateClickstill takes precedence. The dialog gets no lookup picker, and no lookup value is guessed (both ruled out by the triage direction).The console (
@object-ui/app-shell). The console's record-form handler (AppContent'shandleEdit) already opened a create form for a record with no id; the list's New calls it withnull. It now opens that create form pre-filled with the values such a record carries:?form=new) gets them asinitialValues, carried in the state of the history entry that opens it (RECORD_FORM_CREATE_VALUES_STATE);editMode: 'page') gets them as the query-string prefill its create page already reads (/new?due_date=2026-10-20).A record with an id still opens the edit form, and its other keys are ignored.
How the hand-off route was chosen (measured, not assumed)
ObjectCalendarComponentProps.onEditwas declared, and the registered renderer forwards it (HOST_CALLBACKS), butObjectCalendarnever read it. Its renderer's own doc called it inert.ListViewforwards its props to the view component through a trailing spread. In the console that includesonEdit, whose chain is plugin-view'shandleEdit, then the consoleObjectView'sonEdit, thenAppContent'shandleEdit. That is the same handler the header New reaches throughuseObjectActions'create(onEdit(null)). So the console's full create form was already reachable from the calendar through a declared prop, and no new callback is needed.formLink(a child task's parent link). The values now travel in history state rather than in a new reserved URL param. This follows the precedentAppContentalready sets for its post-createfromorigin: no addition to theurlParams.tscontract, while Back and a refresh of that entry keep the values.ListViewhosts (InterfaceListPage,ObjectDataPage) pass noonEditto their list. On those hosts, an object quick-create cannot satisfy offers no empty-day gesture, where it used to offer a dialog that could not succeed.Pins (three boundaries)
showcase_task, the click opens the full form withdue_dateset. At the calendar boundary,ObjectCalendar.quickCreateHandoff-12291.test.tsxasserts thatonEditis called once with exactly{ due_date: '2026-10-20' }and that no dialog opens and nothing is created. The same file drives it through the registeredobject-calendarrenderer withonEditas a trailing prop, which is howListViewhands it down. At the console boundary,AppContent.createSeed-12291.test.tsxasserts?form=new,mode: 'create'andinitialValues: { due_date: '2026-10-20' }.crm_activity-shaped object, the dialog opens and its write carries the day and the auto-filled selects. A table runs one required field per row through both the check and the write, including the server-filled exemptions.ModalForm, opened through the hand-off, submits{ title, due_date: '2026-10-20' }to the adapter'screate. The server is replaced by an adapter double.Verification (HEAD
457ea5d)pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build(29 packages,@object-ui/plugin-calendaramong them): exit 0, under the shared verify lock (VERDICT command-exit 0).pnpm --filter @object-ui/plugin-calendar type-checkexit 0 andpnpm --filter @object-ui/app-shell type-checkexit 0. Each script name was echoed, and each runstsc -p tsconfig.test.json, whoseincludecovers the new test files.pnpm exec vitest run --maxWorkers=2 packages/plugin-calendar/ packages/app-shell/src/console/__tests__/ packages/app-shell/src/utils/__tests__/recordFormNavigation.test.ts:Test Files 90 passed (90),Tests 700 passed | 95 skipped (795).calendar-doc-key-set-8830,object-calendar-record-source-7313,calendar-flat-color-allday-8466,readme-calendar-view-schema):4 passed,48 passed.node scripts/check-changeset-presence.mjsexit 0;check-changeset-no-majorexit 0;pnpm check:control-bytesexit 0;pnpm check:new-line-citationsVERDICT ... 0 new citation(s);pnpm check:doc-fencesandpnpm check:doc-jsonexit 0.node scripts/check-governed-queue-guard.mjs --testover the 10 changed paths: NOT GOVERNED..ts/.tsxfiles (--format json: 7 files, 0 errors). The population comes fromeslint.config.js: its**/*.{ts,tsx}blocks cover them. The config is not type-aware (noparserOptions.project), and its one custom-rule directory reads no files, so this diff cannot change the verdict on any untouched file. Repo-widepnpm lintis CI's.ablation-replace.mjs, the mutation proven on disk and restored to the HEAD blob,git diff HEADempty):ObjectCalendar, forcingquickCreateCanSucceedtotrueturns exactly the seven hand-off cases red, and the ten dialog-keeping cases stay green;AppContent, dropping the history-state values turns the overlayinitialValuescase red, and the real-ModalFormcase too (due_datereceivedundefined); the page-mode case and the two unchanged callers stay green.pnpm check:readme-exportsexits 1 only on self-imports it could not judge, all in READMEs of packages outside the built closure (app-shell,plugin-gantt,plugin-timelineand four more). Theplugin-calendarREADME is judged, and its edit adds no import.Acceptance notes
ObjectCalendar.tsx, "the console's own create path" and tests and changeset. This PR also touchesplugin-calendar'sindex.tsx, but only the doc comment that calledonEditinert. It touches the plugin's README andcontent/docs/plugins/plugin-calendar.mdxto document the hand-off (AGENTS.md commandment 2, docs-driven). It touchesapp-shell'srecordFormNavigation.ts, where the create-values helpers sit besideresolveRecordFormTarget, whichhandleEditalready calls.id,created_at) that are not markedsystemorreadonlywas not read: the backend is not installed here. The server skips those names by name, and the calendar's check does not copy that list. If one does readrequired, the object hands off to the full form, which still creates the row; it is not a dead end.options || choicesandvalue ?? idreads moved verbatim intoquickCreateFill. They are dialect reads in the sense of AGENTS.md commandment 0.1; not touched here. One behaviour moved with them: anullfirst option no longer throws.addRecord: { enabled: true }drawsListView's own Add record button. That button callsonAddRecord, and the console'sObjectViewpassesListViewnoonAddRecord. It was read in source only, not reproduced. Carrier: none.ObjectCalendar's unusedhandleCreateis pre-existing dead code and is left alone.Generated by Claude Code