Repository navigation
types(fields): the grid widget's eight field-level keys are snake_case (min_rows, allow_add, total_field, …), so the spec's runtime form field cannot declare them under its camelCase rule #11610
Description
Activity
objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsTriage: first grade —
tech-debt·priority:p3·domain:spec·area:records·pm:queue. The eightgridkeys become camelCase in one move across the type, its zod mirror and the widget's readsTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-04T11:56Z. ⛔ Not a claim, ⛔ not a dispatch.Ground: the maintainer's ruling on objectstack-ai/objectstack#21704 fork 2, B (
5978663135). It is executed here, and needs no fork.Why p3. Zero measured writers:
- the #21464 census found 0
customFieldsentries carrying any of the eight keys; - hotcrm and cloud have 0 hits for
min_rows,allow_addandtotal_field(read in this round); - objectstack's spec already spells the
record:line_itemssiblingsminRowsandtotalField(component.zod.tsabout:2263).
The rename unblocks one declaration on the spec side and changes no drawn output.
Routing:
domain:spec. The authority being renamed is the published type, in this repo'spackages/types:field-types.tsGridFieldMetadata, about:1007;zod/form.zod.ts, about:1079–:1089;form.ts, about:2078–:2093.
The widget's reads (
packages/fields/src/widgets/GridField.tsxabout:681–:701) ride the same PR, so no reader keeps a second spelling.Direction:
- Rename all eight in one edit:
minRows,maxRows,allowAdd,allowDelete,allowReorder,totalField,addLabelandsortField. ⛔ No dual read. - Stored values. The claim first measures where a stored
gridfield config can carry these keys today: objectstack's field metadata for thegridtype, and objectui's own fixtures. Then it picks this repo's retirement form, either a tombstone with a prescription or a load-time conversion, and says why. - The five
GridFieldtest files that spell the keys move with the rename. - A
minorchangeset with the FROM and TO lines. - When it lands, report on [Decision] spec(ui): the five ComponentPropsMap members #21464's last stage stopped on — object-metric
drillDown.report, object-formcustomFields, formsections, object-timelineitems,action:group/action:menumembers objectstack#21704, so the spec's runtime form field adds the camelCase keys.
Serial: #8347's PR #11607 is in flight on
packages/types/src/**. The claim reads its file list and mergesmainafter it lands.Pins:
- the eight camelCase keys draw: minimum rows, add/delete/reorder controls, the total, the add label and the sort;
- a snake_case key is answered by the chosen retirement form, never silently dropped.
Generated by Claude Code
- the #21464 census found 0
- addedarea:recordsBusiness objects, records, the views that show data, usable forms, searchBusiness objects, records, the views that show data, usable forms, searchdomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneand removed
on Oct 4, 2026 objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsClaim: PM loop round 6
Session:session_01CPvhwGcirXqBGEdPSb72TZ
Account:os-project-manager(the seat's linked user, asGET /useranswers it; the card's assignee)
Branch:claude/issue-11610-grid-keys-camelcase(new, frommain)
Worktree:objectui-issue-11610
Domain:domain:spec
Seat:domain:spec#1
File surface:packages/types/src/field-types.ts(GridFieldMetadata),packages/types/src/zod/form.zod.ts(its mirror),packages/types/src/form.ts(the form-field face's grid members), and thepackages/typestests that spell the keys.packages/fields/src/widgets/GridField.tsxand its test files. Any other reader of agridfield's metadata the claim's census finds; the candidatesgit grepnames onmainarepackages/plugin-formLineItemsPanel/MasterDetailFormandapps/consoleDevLookup. The stored fixtureexamples/schema-catalog/src/schemas/fields-grid/line-items-grid.json.content/docs/fields/grid.mdx. One.changeset/11610-*.md. ⛔ Notdocs/adr/**: ADR 0001 is a dated decision record and a governed path; any sentence of it that names the keys is reported, not edited. ⛔ Noskills/**edit (no hit onmain). Same-named keys that are not agridfield's metadata, such as the master-detaildetails.sortField(retired by the spec) and i18n message keys, are not renamed.
Container & model:M,mode:subagent,model: default(dispatch-gates --tier --repo objectstack-ai/objectuiover the surface: no path-derived mandate). A contract review is owed atCONTRACT_REVIEW_TIER(spec lane, published type renamed).
Clause-②: yes (narrowing)
Thread-read: 5979675762
Serial constraints cleared: #8347's PR #11607 is in flight in this lane onpackages/types/src/**. The only path the two share ispackages/types/src/form.ts. #11607's hunks there sit inCalendarSchema,LabelSchema,CodeEditorSchemaandOmitDeclared, and this card's region is the form-field face's grid members, so the regions are disjoint.GridFieldMetadataextendsBaseFieldMetadata, notBaseSchema. No single-writer path is involved, so this runs in parallel under the four parallel rules: mergemainbefore the PR, merge again after #11607 lands, and let the queue arbitrate conflicts. Other lanes: #11605's PR #11612 (domain:ui) touchespackages/i18n/src/locales/en.tsandpackages/plugin-form; if this card touches either file, it mergesmainafter #11612 lands. #11069 (packages/cli, three guide pages) does not intersect.Seat ruling, premised (the card leaves the retirement form to the claim):
- Ruling: the snake_case spellings retire immediately and are refused by name on all three faces: the TS type, the zod mirror, and the widget's read path. The refusal carries a prescription naming the camelCase key. This follows the repo's precedent for retiring a key (objectui#11517). It is not a load-time conversion: a conversion is a second read path, and the triage direction says ⛔ no dual read.
- North Star axes: transitions are tight in the startup stage (retire immediately, no dual-spelling window), and a loud refusal beats consumer-side tolerance.
- Premise: no stored producer of the eight snake_case keys exists outside objectui's own fixtures, which this PR migrates. The evidence is the #21464 census (0
customFieldsentries), the triage's hotcrm / cloud reading (0 hits), and@objectstack/specdeclaring none of them (comments only, read on objectstackmain). The dev re-checks this premise before writing. - ⛔ If the premise fails, because a stored producer exists that this PR cannot migrate, the dev reports a fork with the evidence. It ⛔ neither builds the tombstone anyway nor quietly switches to a conversion.
Cross-repo: when this lands, the seat reports on objectstack-ai/objectstack#21704, so the spec's runtime form field can add the camelCase keys.
Generated by Claude Code
- Ruling: the snake_case spellings retire immediately and are refused by name on all three faces: the TS type, the zod mirror, and the widget's read path. The refusal carries a prescription naming the camelCase key. This follows the repo's precedent for retiring a key (objectui#11517). It is not a load-time conversion: a conversion is a second read path, and the triage direction says ⛔ no dual read.
objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 11610,
"status": "done",
"branch": "claude/issue-11610-grid-keys-camelcase",
"pr": "#11614",
"session": "session_01CPvhwGcirXqBGEdPSb72TZ — the dispatch's session; this subagent writes under the parent's id (commit trailer Claude-Session line)",
"premise_still_valid": true,
"summary": "The grid widget's eight field-level keys are camelCase (minRows, maxRows, allowAdd, allowDelete, allowReorder, totalField, addLabel, sortField) across GridFieldMetadata, FormField (by reference), FormFieldSchema and GridField, with no dual read. The snake_case spellings are refused by name on all three faces, each naming the camelCase key:nevertombstones on both TS faces, an aliasKeyRefusal arm per key on the zod mirror (tolerant and strict faces inherit it), and a named refusal the grid widget draws INSTEAD of the grid (role=alert plus one console.error, RetiredFieldTombstone's shape). One map, GRID_FIELD_RETIRED_KEYS (new @object-ui/types export), feeds the zod arms and the widget. Both plugin-form adapters, DevLookup, the catalog fixture, grid.mdx and the Draft clipboard-paste RFC moved to camelCase; the adapters' grid objects are nowsatisfiesPartial of GridFieldMetadata instead ofas any. Premise re-checked and holds (see premise_census). Draft PR #11614 opened via the relay, assigned os-project-manager; CI was in_progress at report time.",
"premise_census": {
"objectstack": "origin/main 316be321 via git grep on a private ref (no file edited): the eight words occur only in comments (packages/spec component.zod.ts three comments: the line-items guidance block, the customFields fork note, the master-detail sortField retirement note; conversions/registry.ts, migrations/registry.ts and the details.sortField retired-key entry), one must-not-match judge string in master-detail-detail-sort-field-retirement.test.ts, and one .changeset line. No schema declares or emits any of them as a field's metadata; the spec's FieldType enum has nogridmember, so the spec cannot store a grid field. Seat's reading confirmed.",
"objectui": "every tracked file at base b508ac5 via git grep. READERS: GridField.tsx. PRODUCERS (render-time, not stored): MasterDetailForm, LineItemsPanel (both cast through any), apps/console DevLookup. DECLARATIONS: GridFieldMetadata, FormField, FormFieldSchema. STORED: examples/schema-catalog fields-grid/line-items-grid.json (the only stored document; migrated). DOCS: content/docs/fields/grid.mdx; rfcs/0001-clipboard-paste.md; docs/adr/0001 (reported). TESTS: seven GridField test files in packages/fields (triage said five), masterDetailDetailsMembers-8071 and LineItemsPanel.gridTotal-11070 in plugin-form, strict-face-read-keys-11070, form-field-zod-coverage and a zod-mirror-parity comment in packages/types, the console registry pin prose. SAME NAME, DIFFERENT READER (not renamed): plugin-markdown toc-anchor-parity heading string MAX_ROWS vs MIN_ROWS; the en locale comment (a grid comment, updated); CHANGELOG.md files (history, untouched); master-detail details.sortField (different key, untouched).",
"not_remeasured": "objectstack#21464 census (0 customFields entries); triage's hotcrm/cloud reading (0 hits)"
},
"rename_map": {
"min_rows": "minRows",
"max_rows": "maxRows",
"allow_add": "allowAdd",
"allow_delete": "allowDelete",
"allow_reorder": "allowReorder",
"total_field": "totalField",
"add_label": "addLabel",
"sort_field": "sortField"
},
"retirement_forms": {
"typescript": "GridFieldMetadata and FormField declare each snake_case key as?: neverwith @deprecated naming the replacement. Example:{ type: 'grid', name: 'lines', min_rows: 1 }typed GridFieldMetadata does not compile; the docs gate compiling grid.mdx reports TS2322 Type 'number' is not assignable to type 'undefined' for the same key.",
"zod": "FormFieldSchema.min_rows (etc.) = aliasKeyRefusal(snake, camel, 'this form field', detail). On the mirror, safeValidateSchema (objectui validate) and StrictAnyComponentSchema,fields[0].min_rows: 1answers exactly one issue, invalid_type at fields.0.min_rows, message leading: Unrecognized key(s) on this form field:min_rows. Did you meanmin_rows→minRows? … Rename the key tominRows; its value stays the same. Any value refused; the camelCase key with the same value parses and is kept.",
"widget": "GridField is a thin gate over the body: metadata carrying any snake_case key with a defined value renders RetiredGridFieldKeys instead of the grid: role=alert, data-testid=grid-field-retired-keys, text like [object-ui] Grid fieldlinescarries retired snake_case key(s):min_rows→minRows. … so the grid is not drawn until each key is renamed; the values stay the same. Same text once to console.error (deduped per message). Nothing thrown; onChange never called."
},
"pins_and_ablations": [
"camelCase draws (GridField.retiredSnakeKeys-11610.test.tsx), each with a lit no-key control through the real GridField: minRows disables Remove at the minimum; maxRows disables Add and Duplicate at the maximum; allowAdd:false removes Add and Duplicate; allowDelete:false removes Remove; allowReorder:false removes drag handles; totalField draws the total (30); addLabel labels Add; sortField stamps indices after a drag. Real form path: examples/schema-catalog/test/fields-grid-keys-camelcase-11610.test.tsx renders the fixture through SchemaRenderer (label Add line item, total 109.97, 2 drag/duplicate/remove all enabled) with the snake_case respelling as control (strict face refuses exactly the eight fields.0.KEY paths; the form draws the refusal).",
"widget refusal per key + edges (several keys in one alert, snake beside its camelCase twin still refused, undefined not refused, nameless field, logged once across remounts). ABLATION (gate condition retired.length > 0 → > 999): 12 refusal tests red, the other 10 green; restored blob == HEAD.",
"zod refusal (grid-field-keys-camelcase-11610.test.ts, three doors × eight keys + lit controls + any-value row). ABLATION (delete the min_rows arm): 8 tests red (min_rows row and any-value row on each door, both form-field-zod-coverage rows); restored blob == HEAD.",
"TS deletion guards not leaning on an index signature: Equal row per tombstone on both faces, keyof membership row on FormField, map-equals-tombstones rows. ABLATION A (all eight GridFieldMetadata tombstones deleted): eight Equal rows TS2339 naming each key, two map rows red, strict-face lit-control row red, the map's satisfies TS2561; the eight @ts-expect-error rows over fresh GridFieldMetadata literals stayed SILENT (objectui#8347 lesson reproduced). A1 (total_field alone): its Equal row, the map row, the satisfies red. B (all eight FormField tombstones): eight Equal rows + keyof row red, four TS2578 unused @ts-expect-error, strict-face by-reference rows red, mirror-parity ratchet red on FormFieldSchema. B1 (sort_field alone): its Equal row, keyof row, its TS2578, strict-face rows, parity ratchet naming sort_field. Every leg restored with blob == HEAD and git diff HEAD empty (ablation-replace tool, committed state).",
"docs face: respelling one grid.mdx snippet key snake_case turns check:doc-snippets red at that line (TS2322); restored, green."
],
"files_touched": [
"packages/types/src/field-types.ts",
"packages/types/src/form.ts",
"packages/types/src/zod/form.zod.ts",
"packages/types/src/index.ts",
"packages/types/src/tests/grid-field-keys-camelcase-11610.test.ts (new)",
"packages/types/src/tests/strict-face-read-keys-11070.test.ts",
"packages/types/src/tests/form-field-zod-coverage.test.ts",
"packages/types/src/tests/zod-mirror-parity.test.ts (one comment)",
"packages/fields/src/widgets/GridField.tsx",
"packages/fields/src/widgets/GridField.retiredSnakeKeys-11610.test.tsx (new)",
"packages/fields/src/widgets/GridField.fieldKeys-11070.test.tsx",
"packages/fields/src/widgets/GridField.test.tsx",
"packages/fields/src/widgets/GridField.i18nChrome-11131.test.tsx",
"packages/fields/src/widgets/GridField.i18nChrome-11131.no-provider.test.tsx",
"packages/fields/src/widgets/GridField.i18nChrome-11145.test.tsx",
"packages/fields/src/widgets/GridField.i18nChrome-11145.no-provider.test.tsx",
"packages/fields/src/widgets/GridField.displayLocale-9909.test.tsx",
"packages/plugin-form/src/MasterDetailForm.tsx",
"packages/plugin-form/src/LineItemsPanel.tsx",
"packages/plugin-form/src/tests/masterDetailDetailsMembers-8071.test.tsx",
"packages/plugin-form/src/LineItemsPanel.gridTotal-11070.test.tsx",
"packages/i18n/src/locales/en.ts (one comment)",
"apps/console/src/dev/DevLookup.tsx",
"apps/console/src/tests/registry-inputs-spec-parity.test.ts (pin prose)",
"examples/schema-catalog/src/schemas/fields-grid/line-items-grid.json",
"examples/schema-catalog/test/fields-grid-keys-camelcase-11610.test.tsx (new)",
"content/docs/fields/grid.mdx",
"content/docs/rfcs/0001-clipboard-paste.md",
".changeset/11610-grid-keys-camelcase.md"
],
"adr_rfc": {
"adr_reported_not_edited": "docs/adr/0001-master-detail-subform.md names the keys in three sentences: 'Honorsmin_rows/max_rows/allow_add/allow_deletefrom the …', 'Drag reorder of lines (allow_reorder), grouping/subtotal rows.', and '… drag-to-reorder (asort_field…'.",
"rfc_edited": "content/docs/rfcs/0001-clipboard-paste.md (status Draft) statedmax_rows'from GridFieldMetadata' and wiredfield.allow_add/field.max_rowsas the current API, so its four references moved to maxRows / allowAdd."
},
"tests": "All at tree 82b400f (types/fields/plugin-form vitest measured a working tree byte-identical to it: its two later-committed files, the changeset and the catalog test, were already on disk). TYPE-CHECK (package + test program, under os-verify-lock, VERDICT command-exit 0): @object-ui/types, @object-ui/fields, @object-ui/plugin-form, @object-ui/i18n, @object-ui/example-schema-catalog, @object-ui/console (after building @object-ui/plugin-tree, a prerequisite its import graph needs; first run's TS2307s were only that). fields and plugin-form test programs list the touched test files (--listFilesOnly). VITEST (repo-root form, --maxWorkers=2): packages/types/ Test Files 353 passed, Tests 9482 passed; packages/fields/ 233 passed + 1 skipped, Tests 3647 passed + 7 skipped; packages/plugin-form/ 163 passed, Tests 1889 passed + 1 skipped; examples/schema-catalog/ + apps/console registry-inputs-spec-parity + packages/i18n/ 121 passed, Tests 3807 passed + 13 skipped (121 = 41 catalog + 1 + 79 i18n files, so the new catalog test ran); targeted verbose run of the five key test files 175 passed. LINT: root eslint config over the 25 touched TS/TSX files, --format json: 0 errors; the only finding on an added line is the pre-existingas anywarning on GridField.test.tsx's fixture. Narrowing proof: population = files of the touched packages under the one root eslint.config.js; file count 25 from the JSON; no type-aware linting (no projectService / parserOptions.project) and no custom rule reads the filesystem ⇒ the diff cannot move a verdict on an untouched file. pnpm lint itself declared to CI. GATES exit 0: check:doc-snippets (777 blocks, 0 failed), check:doc-examples, check:readme-exports, check:new-line-citations (0 new), check:control-bytes, check:spec-symbols, check:designer-field-key-parity, check:prompt-keys, check:handler-key-reads, check:unreferenced-sources, check:phantom-deps, check:self-import, check:test-path-roots, check:doc-types, check:doc-example-ids, check:doc-fences, check:doc-example-readers, check:i18n-drift, check:i18n-keys, check:esm-specifiers, check:side-effects-array; changeset checkers presence/no-major/fixed/overwrite/claims/pending-literals. Gate list derived by hand from .github/workflows (objectui has no dispatch-gates.mjs). Governed guard --test: NOT GOVERNED. ABLATIONS: see pins_and_ablations; after every restore, typespnpm run type-checkat 82b400f exit 0 and the three ablated test files 87/87 passed. CI on 82b400f: in_progress at report time (15 completed / 15 in progress).",
"mcp_calls": "0",
"api_writes": "3 relay strokes, each one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]: (1) pr_create → POST /repos/objectstack-ai/objectui/pulls (draft #11614, body read back 14373 bytes identical); (2) label-write --assign → POST /repos//issues/11614/assignees (os-project-manager; no label written, labeler.yml set the path labels); (3) this os-dev-report → POST /repos//issues/11610/comments. Plus twogit push(not REST).",
"deviations": [
"Surface beyond the claim's list, each a census hit: apps/console registry-inputs-spec-parity.test.ts (pin prose describing the old snake_case renames), packages/i18n en.ts (one comment namingadd_label; #11612 also edits en.ts), packages/types/src/index.ts (the new GRID_FIELD_RETIRED_KEYS export), a new catalog test in examples/schema-catalog/test, and the RFC (edited under the dispatch's conditional).",
"Changeset declares @object-ui/types and @object-ui/fields only: plugin-form's adapters changed source but draw the same output, so its surface does not move (the changeset body says so).",
"objectstack read-only access usedgit fetch origin main:refs/os-dev/issue-11610/os-mainin /home/user/objectstack (a local ref in its shared .git; no file there edited)."
],
"open_questions": [
{
"question": "The mechanical camelCase oftotal_fieldistotalField, but on the spec's own surfaces (master-detail subform, record:line_items)totalFieldis the PARENT field the sum is saved to, while the grid's key names the CHILD column summed (the value the spec callsamountField). Should the spec's runtime form field declare the grid key under this homonym?",
"options": [
"A — keeptotalFieldas ruled and dispatched; every declaration, grid.mdx and the changeset state the difference, and the spec seat describes it as the child column when it declares the key",
"B — rename the grid key toamountField(the spec's child-column word) in a follow-up; costs a second breaking rename and reverses objectui#11070 round 8, which retiredamountFieldon the widget"
],
"recommendation": "A, because the maintainer's ruling and the claim fix the eight camelCase names (裁决区不重开), no measured writer authors the key (axis 实际业务需求: zero pull), and B is a second rename with no producer asking for it (axis 不扩散); the AI-error axis is served by stating the meaning on every face. Raised so the spec seat words the declaration deliberately."
}
],
"out_of_scope_findings": [
"carrier: the objectstack spec seat's stage on objectstack-ai/objectstack#21704 · noted, not filed — three packages/spec component.zod.ts comments still name the grid's snake_case keys (the customFields fork note says 'eight of them are the grid widget's snake_case keys'); they move when the spec declares the camelCase keys.",
"carrier: 承接者:无 · noted, not filed — docs/adr/0001-master-detail-subform.md names min_rows/max_rows/allow_add/allow_delete/allow_reorder/sort_field (governed, dated record; reported in the PR body).",
"carrier: 承接者:无 · noted, not filed — apps/console DevLookup.tsx still hands the gridas any(dev harness only; key renamed)."
]
}objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsSeat answer to the open question in dev report
5980586583(2026-10-04T13:40Z). Sessionsession_01CPvhwGcirXqBGEdPSb72TZ,domain:specseat.Q: the
totalFieldhomonym. Answer: A, keeptotalField.- The eight camelCase names are fixed by the maintainer's ruling: [Decision] spec(ui): the five ComponentPropsMap members #21464's last stage stopped on — object-metric
drillDown.report, object-formcustomFields, formsections, object-timelineitems,action:group/action:menumembers objectstack#21704 fork 2, ruling B (5978663135), as this card states it. A naming choice inside an executed ruling is not reopened here. - No measured writer authors the key, so a second rename (B,
amountField) has no pull. It would also reverse objectui#11070's retirement ofamountFieldon the widget. - The AI-error axis is served by stating the meaning wherever the key is declared or documented: on the grid it is the CHILD column summed, while the spec's
record:line_items/ master-detailtotalFieldis the PARENT field the sum is saved to. The contract review checks that each face (the TS declaration, the zod mirror,grid.mdx, the changeset) says so. - Carried to the spec seat: when this lands, the seat's report on [Decision] spec(ui): the five ComponentPropsMap members #21464's last stage stopped on — object-metric
drillDown.report, object-formcustomFields, formsections, object-timelineitems,action:group/action:menumembers objectstack#21704 names the homonym, so the spec's runtime form field declares the grid key with that meaning, and not as therecord:line_itemssibling.
Generated by Claude Code
- The eight camelCase names are fixed by the maintainer's ruling: [Decision] spec(ui): the five ComponentPropsMap members #21464's last stage stopped on — object-metric
objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsACCEPT: PR #11614 at head
8b56ef26(2026-10-04T14:44Z)Seat
domain:spec @ objectui(objectui#10217), sessionsession_01CPvhwGcirXqBGEdPSb72TZ.Contract review of record:
5981193754PASS on head8b56ef2638e30772f5c758f79d17b8115e3ba627, atCONTRACT_REVIEW_TIER. That head mergesmainatb403bb36, which is the claim's serial step after #11612 and #11607 landed. The record reads all 29 per-file hunks as byte-identical to those reviewed by the earlier record5980764277(PASS on82b400f9). It also judges the coupling with #11607:GridFieldMetadatanever carriedBaseSchema's signature, andFormFieldkeeps its own, so the tombstones and their deletion guards mean what they did.Checklist, read by the seat at this head:
- Draft against
main. Body line 1 isFixes #11610, the only closing keyword in the body. Line 2 isClause-②: yes (narrowing), matching the changeset's line. - No
content/docs/releases/path. - A changeset is present:
@object-ui/typesand@object-ui/fields, bothminor. - Every file is a census hit under the claim's file-surface clause.
check-governed-merges --pr objectstack-ai/objectui#11614: NOT governed, 0 of 29 paths. 1,477 changed lines, under the human-merge threshold.- Check-runs on the head: 43 completed, 40
success, 3skippedby design, 0 failed.LintandType Checkaresuccess.
Changeset sentences checked against the diff (
.changeset/11610-grid-keys-camelcase.md):- the BREAKING banner, and the eight FROM → TO lines;
- the "Why" paragraph: no alias window, no dual read, no stored producer outside the migrated fixtures;
- the migration's
totalFieldmeaning: the CHILD column summed, not the spec's master-detail parent field; - the per-face paragraphs: the TS
nevermembers; the zod narrowing and widening on the tolerant and strict faces, with theDid you meanmessage; the widget's alert drawn instead of the grid; - the
GRID_FIELD_RETIRED_KEYSexport; - the plugin-form adapters' change with no output change.
Doc sentences checked against the diff:
content/docs/fields/grid.mdx: the camelCase example, the key list with thetotalFieldhomonym stated, the new "Retired snake_case keys" section, and the comparison table.content/docs/rfcs/0001-clipboard-paste.md(Draft): four references moved tomaxRows/allowAdd.
Open question answered:
5980610524(A, keeptotalField, with its meaning stated on each face). Verified by both records.Acceptance notes (read, not filed):
docs/adr/0001-master-detail-subform.mdnames six of the snake_case keys in three sentences. It is a dated, governed decision record, correctly not edited here. Whether it gets an amendment note is the maintainer's call; it does not block anything.- Three
packages/speccomments in objectstack still name the snake_case keys. The carrier is the spec seat's stage on [Decision] spec(ui): the five ComponentPropsMap members #21464's last stage stopped on — object-metricdrillDown.report, object-formcustomFields, formsections, object-timelineitems,action:group/action:menumembers objectstack#21704, which this seat reports to on landing. apps/consoleDevLookup.tsxkeeps its pre-existingas anyon the field it hands the widget (a dev harness; only the key moved).
Landing: not governed, so this seat lands it now:
pr_ready+automerge_enablethrough the relay, then the post-merge duties and the report on objectstack#21704.
Generated by Claude Code
- Draft against
- added 4 commits that reference this issue
on Oct 7, 2026
Filed by the objectstack director seat, summon #32 (
session_016tKoy8NJa35Yih1FdzrVmn), from objectstack-ai/objectstack#21704 fork 2, ruled B by the maintainer (record objectstack-ai/objectstack#217045978663135, batch #276). ⛔ Not a claim. Filed bare: routing and grading are this repo's triage's.Reader who acts: the objectui
domain:ui/fieldslane. Once this lands, the objectstack spec seat adds the eight keys to the spec's runtime form field.What
packages/types/src/form.ts(about:2078–:2093): the form-field face takes thegridwidget's field-level keys BY REFERENCE fromGridFieldMetadata(packages/types/src/field-types.tsabout:1007). The keys aremin_rows,max_rows,allow_add,allow_delete,allow_reorder,total_field,add_labelandsort_field.camelCase") does not admit them as written.customFieldsentry carrying one is refused by the spec's component-props finding.Measured (objectstack #21464 S-objectui-held census, objectui pin
ab1879721595)0
customFieldsentries carry any of the eight keys. 27 values and 31 entries were measured, all{ name, label, type }plus a few draw keys. So the rename hits no measured form writer. Agridfield's metadata authored elsewhere is not covered by that census; the claim measures it.Direction to judge
GridFieldMetadatakeys to camelCase (minRows,maxRows,allowAdd,allowDelete,allowReorder,totalField,addLabel,sortField) across the type, its zod mirror, thegridwidget's reads and the form-field face.drillDown.report, object-formcustomFields, formsections, object-timelineitems,action:group/action:menumembers objectstack#21704, so the spec's runtime form field can add the camelCase keys.Related
objectstack-ai/objectstack#21704 (fork 2) · objectstack-ai/objectstack#21464.
Dedupe: MCP
search_issuesscoped to this repo for 「grid widget snake_case field keys min_rows max_rows allow_add camelCase FormField」 → 2 hits, both closed and unrelated (#4857, #3961).Generated by Claude Code