Repository navigation
fix(types): declare a form field's avatarField; avatar_field stays refused as its second spelling (objectui#12130) - #12158
Conversation
…icker, before declaring it (objectui#12130) A `user` or `lookup` field with `picker: 'search'` draws each candidate's avatar from `image` without the key, and from the named record field with `avatarField`. Both legs read a drawn `img` (images reported as loaded), so neither passes on a picker that draws no avatar. Claude-Session: https://claude.ai/code/session_01V8qYZseSLfKH3V8tPu2f73 Co-authored-by: Claude <noreply@anthropic.com>
… refused as its second spelling (objectui#12130) The `lookup`, `master_detail` and `user` widgets read `avatarField` off a form's `fields[]` entry, and with `picker: 'search'` each candidate draws its avatar from that record field (the effects pin committed before this). The strict authoring face refused it. `FormField` and `FormFieldSchema` now declare it in parity, typed as the widgets read it (`string`): the spec's `FieldSchema` declares neither spelling. `avatar_field`, which the widgets read first, stays undeclared and moves to the census ledger's second-spelling class (AGENTS.md #0.1). The census ledger's OPEN rows are now `step` and `className`. Claude-Session: https://claude.ai/code/session_01V8qYZseSLfKH3V8tPu2f73 Co-authored-by: Claude <noreply@anthropic.com>
|
changeset-claim-re-read ✅ Nothing to re-readNo pending changeset names a file this change touches, no address in this pull request's own prose points at a line this change moves, and no changeset here negates a package it declares. An earlier revision did. That request to re-read does not apply to the current diff. This comment is updated in place on every re-run rather than posted again, so the thread does not grow one per push. Compared the checked-out tree with |
✅ 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
|
…ist, and the now-redundant Supersedes bullet (objectui#12130) Round 1's pending changeset listed `avatarField` / `avatar_field` as still undeclared and open; this PR declares `avatarField`, so the sentence is corrected in the same PR (claim amendment 6113007901). The list now reads `step` and `className`. The round-2 changeset's Supersedes bullet existed only to cover that sentence and is dropped. Prose only; front matter untouched in both files. Claude-Session: https://claude.ai/code/session_01V8qYZseSLfKH3V8tPu2f73 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: Read: card #12130 (body and all 11 comments, among them the round-2 claim ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Part of #12130
Clause-②: yes (widening): declaring
avatarFieldwidens the strict authoring face's accept set by one key, and its value check narrows the tolerant face, where the key used to be stripped unjudged.Round 2 of objectui#12130: declare
avatarFieldon the form-field face, and keepavatar_fieldrefused as its second spelling. This PR leaves the card open.stepandclassNamestay OPEN in the census for round 3.Implemented by the os-dev dispatched from the
domain:spec@ objectui seat, sessionhttps://claude.ai/code/session_01V8qYZseSLfKH3V8tPu2f73, claim comment 6112572465. Base:mainat0665c3b(round 1, PR objectui#12154).What changed
1. The premise, measured first (commit
06d7231, before any declaration;examples/schema-catalog/test/form-field-channel-effects-12130.test.tsx). Foruserand forlookup, a field withpicker: 'search'is rendered through the realSchemaRenderer,formrenderer and@object-ui/fieldsregistrations. The candidate row carries an avatar inimageand another inphoto. Without the key, the search picker's person row drawsimage. WithavatarField: 'photo', it drawsphoto. Both legs read a drawnimg, so neither leg passes on a picker that draws no avatar. happy-dom never loads an image, so the case reports images as loaded, the technique ofPeoplePicker.outputFls-10433.test.tsxin@object-ui/fields.2. The declaration (
packages/types/src/form.tsFormField,packages/types/src/zod/form.zod.tsFormFieldSchema, in parity):avatarFieldstring(defaultimage)lookup,master_detail,userThe notes beside both declarations now say that
avatar_fieldstays undeclared, as the second spelling (AGENTS.md #0.1).3. The census (
form-field-channel-census-12130.test.tsx). TheavatarFieldledger row leaves OPEN.DECLARED_BY_12130gainsavatarField: ['lookup', 'master_detail', 'user'], which the census re-derives on every run. Theavatar_fieldrow moves to the second-spelling class besideallow_create,lookup_columns,lookup_page_sizeandreference_field. Its readers,LookupFieldandUserField, are unchanged.4. The face pin (
packages/types/src/__tests__/form-field-channel-keys-12130.test.ts).avatarFieldparses on the strict face, and a misspelled sibling is still refused at the field. The tolerant face keeps the value. A non-string value is refused at that key on both faces.avatar_fieldis refused by name. The TypeScript member is typed, not the index signature'sany, and equalsstring | undefined.form-field-zod-coverage.test.ts's declared-key list gains the key.5. One
.changeset/12130-avatar-field.md(@object-ui/types: minor, with the breaking semantics stated,Clause-②: yes (widening)). Patch 1 (5b7f24d, claim amendment 6113007901) also corrects round 1 pending.changeset/12130-form-field-channel-keys.md: its 「Still undeclared, and listed as open in the census」 list dropsavatarField/avatar_fieldand now readsstepandclassName. Prose only; front matter untouched.Readings per ruling and hypothesis
0665c3b, quoted:fieldMeta?.avatar_field ?? fieldMeta?.avatarField ?? 'image'inLookupFieldandavatar_field: meta?.avatar_field ?? meta?.avatarField ?? 'image'inUserField. Both readavatarFieldwhenavatar_fieldis absent. The value changes what the search picker draws, as measured in item 1 for each reader.lookup,master_detailanduser. The census's exact-equality row asserts this.master_detailrenders through thelookupwidget, so the reader attribution is stillLookupField/UserField.@objectstack/spec17.7.0) has aFieldSchemaof 75 keys that includes neither spelling (control:lookupPageSizereads present). So the type is objectui's own (string), likedefaultName. The addition: the spec names camelCaseavatarFieldonce, inBULK_PARAM_WIDGET_CONFIG_KEYS, the refusal guidance list ofBulkActionParamSchema. That list does not nameavatar_field. So where the spec spells this widget-config key, it spells it camelCase.stepandclassName. Both are untouched.Acceptance notes
.changeset/12130-form-field-channel-keys.md(pending) said 「Still undeclared, and listed as open in the census:step, …;avatarField/avatar_field; andclassName.」, which this PR makes false foravatarField. Patch 1 (5b7f24d, claim amendment 6113007901) removes 「avatarField/avatar_field;」 from that list and drops the round-2 changeset then-redundant 「Supersedes」 bullet.check-changeset-overwritereports the modified file (report-only, exit 0).LookupFieldreadsavatarFieldon everylookup/user/master_detailrender, but the value is drawn only by the search picker (picker: 'search', which is theuserwidget's default) and by its selected-value chips. The chips fall back toimagewhen the named field is empty. With the classic quick-select, the key is accepted and has no visible effect. This is the same bound assubtitle.content/docs/fields/user.mdxstill teachesavatar_field: 'image', which the strict face refuses. That page,UserFieldMetadata.avatar_fieldand the snake-first reads belong to objectui#12157, the snake_case retirement. None is touched here.BULK_PARAM_WIDGET_CONFIG_KEYScalls its keys 「keys of a FIELD (FieldSchema,data/field.zod.ts)」. Several keys on the list are notFieldSchemakeys:avatarField,picker,subtitle,idField,crop,capture,defaultName. The refusal it gives for a bulk param is still right, so this is only an observation.Verification (code at
2083a81; patch 15b7f24dchanges changeset prose only)vitest run packages/types/gave 369 files and 10181 tests passed.vitest run examples/schema-catalog/gave 45 files and 2343 passed.vitest run scripts/__tests__/gave 180 files passed and 2 skipped (5489 tests passed).pnpm --filter @object-ui/types type-checkandpnpm --filter @object-ui/example-schema-catalog type-checkboth exit 0, after the schema-catalog dependency closure was built (28 of 28 turbo tasks).tsc -p tsconfig.test.json --listFileslists both touched test files in each test project. A probe also compiled two literals:FormFieldwithavatarField: 'photo'compiles, and withavatarField: 3it is TS2322.pnpm --filter '@object-ui/cli...' buildexits 0, thenpnpm checkexits 0 with 「286 validated, 147 recognised but not validated, 0 did not validate」.objectui validate(the built CLI) answers as follows. Auserfield withavatarField: "photo"is valid (exit 0). The same document withavatar_fieldfails with 「Unrecognized key: "avatar_field"」 (exit 1). WithavatarField: 3it fails with 「expected string, received number」 (exit 1).check:element-data-source-declaration,check:designer-field-key-parity,check:spec-symbols,check:phantom-deps,check:control-bytes, andcheck:new-line-citations(「0 new citation(s)」). Derived and added:check-test-path-roots,check-vi-mock-override-shape,check-lint-coverage,check-type-check-coverage,check:unreferenced-sourcesandcheck:pending-changeset-literals. The doc gates over the built types also each exit 0, afterturbo run buildover thecheck-doc-snippet-types --build-filterset (35 of 35 tasks):check-doc-snippet-types,check-doc-example-types,check-doc-json-fences(「Every judged JSON node document on the scan surface passes the strict authoring face.」),check-doc-component-types,check-prompt-component-keys,check:doc-fencesandcheck:doc-example-ids.5b7f24d, each exit 0:check-changeset-presence(「this change declares 1 changeset(s): .changeset/12130-avatar-field.md」),-claims(「No pending changeset names a file this change touches.」),-no-major,-overwrite(report-only: 「1 changeset(s) added, 1 modified, 0 deleted」, naming.changeset/12130-form-field-channel-keys.md,@object-ui/types: minorat base and now; 「Report-only: this gate does not fail the build on the findings above.」),-fixed,check:pending-changeset-literalsandcheck:control-bytes.--no-inline-config --format json) on the 6 changed.ts/.tsxfiles: 6 files, 0 errors, 9 warnings. All 9 areno-explicit-anyinform.ts, and the BASE blob ofform.tsreads 9 too. The population iseslint.config.js's**/*.{ts,tsx}minus its global ignores. No block of that config setsparserOptions, so type-aware linting is off and this diff cannot move a verdict on an untouched file.ablation-replace.mjswith its own restore trap plus a shell trap. Afterwards, the blob hash equals HEAD's andgit diff HEADis empty.avatarFieldrenamed inFormFieldSchema. Anchor x1 to x0, blobf45d49bc3b9dtof1f30566b77f. The census goes red with 2 failed:unexpected: ["avatarField ← entry@fields/src/widgets/LookupField.tsx, entry@fields/src/widgets/UserField.tsx"], and theavatarFieldrow ofDECLARED_BY_12130. The census reads@object-ui/types/zodthrough the vitest alias tosrc, so no build sits between the mutation and the run.FormField.avatarFieldwidened toany. Blob6c094d6866dctoee6088169f1e.tsc -p tsconfig.test.jsonexits 2 with two TS2344 errors, at the face pin'sassertionChannelMembersAreTypedandassertionLocalMembers.Generated by Claude Code