feat(types,plugin-grid)!: object-grid and list-view conditionalFormatting take the spec list view's { condition, style } rule only; the native dialect is refused by name (objectui#11533) - #11540
objectstack-fleet[bot] merged 4 commits into
Conversation
…ting take the spec list view's { condition, style } rule only; the native dialect is refused by name (objectui#11533)
The grid's and the list view's shared rule is the spec ListViewSchema
rule element by reference, .extend()-ed with objectui's string condition
arm and seven retirement tombstones (field, operator, value, expression,
backgroundColor, borderColor, textColor). The TS twin
ConditionalFormattingRule extends SpecConditionalFormattingRule with the
same seven keys declared ?: never; ObjectUIConditionalFormattingRule is
deleted. plugin-grid's input description stops advertising both forms.
resolveConditionalFormatting is untouched: stored rules still paint.
WIP step 1 of the card: faces only; tests, docs and changeset follow.
Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
…t-view rule refusals on every zod face and the TS face, and that a stored native rule still paints (objectui#11533) - grid-list-view-conditional-formatting-11533.test.ts: each retired key refused by name on ObjectGridSchema, ListViewSchema, and the tolerant and strict faces on a list-view node and an object-view table slot; the spec rule by reference (style identity, condition arms, the one shared declaration); the TS face by @ts-expect-error and keyof parity. - gridRowDecorationMembers-8071: the retired members are re-framed as stored-rule reads; a new describe pins refused-at-authoring beside painted-at-render, with the respelling as the control. - ListView.storedRuleDialect-11533: a stored list view's native rules reach the grid untouched while ListViewSchema refuses them. - Respelled: round 6 MIRRORED row, the export-options neighbour control, the 10946 list-view rule pin, the object-view forwarding sentinel. Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC Co-authored-by: Claude <noreply@anthropic.com>
… pin prose for the grid and list-view rule retirement (objectui#11533) - .changeset/11533-grid-listview-rule-dialect-retired.md: types and plugin-grid minor, BREAKING spelled out, before/after table, respelling, the bag left to the spec row, what still paints. - Dated notes on 10946 (the list view's rule is no longer a union) and 11522 (the evaluator keeps its arms as a stored-rule read, not because the grid and list view still declare them). - schema-reference: an ObjectGridSchema conditionalFormatting row. - zod README: conditionalFormatting leaves the broader-than-spec list. - registry-inputs-spec-parity: the object-grid member-pin prose says the retired members are stored-rule reads now. - KanbanImpl: the getCardStyles comment's reason, made false by this change, is corrected (comment only). Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC Co-authored-by: Claude <noreply@anthropic.com>
…y` (objectui#11533) Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC Co-authored-by: Claude <noreply@anthropic.com>
|
changeset-claim-re-read
|
✅ 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 #11533 (body; comments Check-runs on the head: first read 2026-10-03T05:54:11Z, 42 runs, 29 ① Derived judgmentsAccept-set and public-surface changes the diff implies, each read against the head sources:
② Semver levelChangeset Export loci, each sentence checked at the head with the care the 11522 round-1 FAIL set: Clause-②: no (narrowing). RIGHT. Every face moves one way (judgments 1 to 6, 10); Pending sweep (read, not recalled; 2,704 entries, grepped for
③ Boundary flags
Dev deviations, each: (1) files outside the claim's enumerated loci, judged below; (2) the bag not narrowed, judgment 14; (3) H1's one-schema fold not taken, judged below; (4) objectstack read from a sparse clone in the dev's scratch, no git command in the shared checkout, and one Out-of-scope findings, each answered: (a) Boundary note, each item:
Dev NOT MEASURED items: hotcrm, accepted on triage's own zero ( Implemented-by: VERDICT: PASS |
Fixes #11533
Clause-②: no (narrowing)
conditionalFormattingonobject-grid(ObjectGridSchema, and so theobject-viewtableslot built from it) and onlist-viewtakes ONE rule dialect, the spec list view's{ condition, style }. The native{ field, operator, value }rule, itsexpressiontemplate predicate and a colour written at the top level of a rule (backgroundColor,borderColor,textColor) are retired with no alias window and refused by name. This executes triage's ruling5965301211(retire, as objectui#11522 ruled for the kanban board in5963861071; never widen the spec's list-view member). Stored stock is not authoring:resolveConditionalFormattingkeeps every arm, so a grid or list view STORED with a native rule still paints, byte for byte as before.What changes
ObjectGridSchema, anobject-viewtableor alist-view8bfc0012){ condition, style }(string, envelope or''condition){ field, operator, value, backgroundColor }field,operator,valueandbackgroundColor, each naming the retirement{ expression, backgroundColor }invalid_unionat the ruleexpressionandbackgroundColorby name{ condition, backgroundColor }invalid_unionat the rulebackgroundColorby name, andstyle(required){ condition, style, backgroundColor }invalid_unionbackgroundColoron every face{ condition, style, label }unrecognized_keyswith the spec rule's own messageThe faces read: the
ObjectGridSchemaandListViewSchemamirrors, andsafeValidateSchema(tolerant) andStrictAnyComponentSchema(strict), each on alist-viewnode and on anobject-viewtableslot. Both columns were read with a throwaway probe on each tree; it was deleted and never committed.Not judged here, and unchanged: the authored
object-gridnode'spropertiesbag. Its members are@objectstack/spec'sComponentPropsMap['object-grid']row by reference (objectui#11276). The installed 17.5.0 row typesconditionalFormattingasunknown, so the bag accepted all six rules before and after. It carries no objectui copy of the rule, and objectui does not narrow a spec row (see Acceptance notes).@object-ui/types,src/zod/objectql.zod.ts). The grid's and the list view's shared ruleConditionalFormattingRuleSchema(module-private, one declaration for both mirrors) is no longer a union. It is the specListViewSchema.conditionalFormattingelement (SpecListViewRuleSchema, now declared once above the grid mirror and.extend()-ed by both this rule and the kanban rule), so it inherits the spec rule's strictness and itsstylemap. Two things are layered on top:conditionstaysSpecRuleConditionSchema(objectui'sz.string()arm before the spec slot, so a string is not canonicalized and''is still accepted, objectui#10946), and seven retirement tombstones:field,operator,value,expression,backgroundColor,borderColor,textColor, through agridRuleKeyRetiredmessage builder. The grid member's.describe()and the "intentionally broader than spec (migration deferred)" docblock lines go;conditionalFormattingleaves that list.objectql.ts).ConditionalFormattingRuleis an interface that extendsSpecConditionalFormattingRuleand declares the same seven keys?: never.ObjectUIConditionalFormattingRuleis deleted and dropped from the root barrel (TS2305 for an importer). tsc reports each retired key at the key (TS2322).@object-ui/plugin-grid. Theobject-gridregistration'sconditionalFormattinginput description stops advertising both forms: it describes the one rule and names the retired spellings. No render path changes.conditionalFormattingrow incontent/docs/api/schema-reference.md's ObjectGridSchema table;packages/types/src/zod/README.mddrops the member from the broader-than-spec list.11533-grid-listview-rule-dialect-retired.md):@object-ui/typesand@object-ui/plugin-gridminor, BREAKING in the first line, before/after table, respelling, the bag and the paint stated as unchanged.Mechanism hypotheses, measured
H1, the faces and the shared union (a type-checker census, not grep). A TypeScript LanguageService over all 5,034 tracked TS sources, under the root tsconfig's paths (which resolve
@object-ui/typesto source):findReferenceson each declaration, plus everyconditionalFormattingidentifier resolved to the declaration it binds.ConditionalFormattingRuleSchemaObjectGridSchema.conditionalFormattingand theListViewSchemaoverride (2 refs); through the grid mirror's.shapeit also builds theobject-viewtableslotObjectGridBlockSchematakes six other mirror members by reference, not this oneConditionalFormattingRuleObjectGridSchema.conditionalFormatting,NamedListView.conditionalFormatting, the root barrel, the 10946 relay pin, a docblock link in the kanban ruleObjectUIConditionalFormattingRuleObjectGridSchema.conditionalFormattingmember (12 refs)ObjectGrid.tsx(5 reads, each handed to the resolver or the harvest as an unknown array), plugin-view's route-2 relay, testsListViewSchemamember (10 refs)ListView.tsx(evaluateConditionalFormatting's rules parameter, the harvest, the relay into its grid node), app-shellObjectView.tsx's relay (its other operand isany), testsresolveConditionalFormattingConditionalFormattingRuleLikein@object-ui/core)listViewPredicates(the predicate-field harvest)readonly unknown[]Can the kanban rule and the grid/list rule become one schema? Measured: same base element (now one declaration), same
conditionarms (pinned arm by arm), samestyle(identity, pinned). They differ in exactly two things: the carrier and card each message names, andexpression, which the grid and list rule carried and the kanban rule never did (kanban refuses it through the spec's own unrecognized-keys message with its "Did you meanexpression→condition?"). Folding them would changeobject-kanban's landed refusal text and itsexpressionpath, a surface objectui#11522's contract record judged on its landing head. Not done here; the report carries the option.H2, the census. Two instruments. (1) An AST pass (TypeScript parser) over the 59 TS files that mention
conditionalFormattingorConditionalFormattingRule: every object literal with afield+operatorpair, a top-level colour beside a predicate key, or anexpressionbeside a style key: 50 hits, each triaged by hand. The filter literals it also caught (UserFilterTabRuleSchema,ReportFilterSchema, filter fixtures) are its live control. (2) A text pass over every other tracked file that mentions the member: the 500 characters after each token, for a retired key in any syntax: 1 window, the kanban row of the schema reference, which teaches the forms as refused.unmirrored-declared-round6-6152.test.tsvalidrow) and 1 native with a wrong operator (theinvalidrow)validrespelled{ condition: "record.status == 'open'", style: { backgroundColor: '#fee2e2' } };invalidreplaced by{ condition: "record.status == 'late'", style: 'color: red' }, because its wrong value (an operator outside the native enum) has no member left to be wrong inexport-options-spec-parity.test.ts{ condition, style }line and the non-array refusalobjectViewHostSurface.test.tsx(plugin-view){ field, operator: 'eq', value, color }{ condition: "record.stage == 'won'", style: { color: '#ff0000' } }gridRowDecorationMembers-8071.test.tsx(plugin-grid)spec-expression-wire-slots-10946.test.tsconditionstraight off the rule's shapelistConditional.test.ts,predicate-fields.test.ts(core)ConditionalFormattingEditor.test.tsx(app-shell)kanban-conditional-formatting.test.ts,unmirrored-declared-round3-6152.test.tsexamples/,apps/consolenon-test code,skills/object-grid/list-viewin 5examples/files,object-gridin 1 console file and 3skills/files)examples/at4916168(my own sparse clone)conditionalFormatting(app-showcasefield-zoo.view.ts) is{ condition, style }; controls fire (object-grid/list-viewview files,{ field, operator, value }filter literals)objectstack-ai/hotcrmH3, stored rules still paint. Before and after, through the real
ObjectGridand through the realListViewhanding its node to the real grid, for all six rules of the table: the readings are byte-identical, for examplenative: Alpha=[background-color: rgb(1, 2, 3);] Beta=[null]on both trees and both renderers. Read paths that could validate a stored view: none on the render path (SchemaRendererruns only the dev-only structuralvalidateSchema; nothing parses a grid or list-view node). Outsidepackages/types, the only non-test callers of objectui's zod faces are the CLI'sobjectui validate/objectui check(authoring doors) and three unrelated schema imports (a dashboard widget, a dashboard designer door, a map config). Studio'sclientValidationjudges a view draft against@objectstack/spec/ui'sViewSchema, not objectui's face, so this change does not reach it; the installed spec's list-view rule already refuses the native rule there (unrecognized_keysnamingfield,operator,value,backgroundColor), at BASE as now. Premise held; nothing for the seat.H4, the refusals. One arm, so each refusal sits at the retired key's own path; the messages are byte-identical on the tolerant and strict faces and on both carriers. Quoted, with backticks as in the source:
conditionalFormatting.N.field(operatorandvaluethe same, key swapped):.expression:.backgroundColor(borderColorthe same;textColorendsstyle: { color }):conditionalso drawsinvalid_unionat.conditionandinvalid_typeat.style(both required); an undeclared key draws the spec rule's own message, "Unrecognized key(s) on this conditional formatting rule:label. …".The pins
packages/types/src/__tests__/grid-list-view-conditional-formatting-11533.test.ts(new, 44 tests): on each of the six faces, the{ condition, style }control (string and envelope); the native rule refused at each retired key with the message starting with the key's name and containingRETIRED (objectui#11533),{ condition, style }and the respelling, while the accepted rule at index 0 of the same document draws no issue;expressionrefused with its respelling; the flat CEL rule refused at the colour key with noinvalid_unionat the rule; all three colour keys refused beside astyle; the spec's unrecognized-keys message. The reference: the two mirrors share one rule object,styleis the spec element's own member (identity),condition's second arm is the spec slot and both arms are the kanban rule's, with the control thatconditionis not the bare slot. The TS face:@ts-expect-erroron each retired key, on a native rule in anObjectGridSchemaand aListViewSchemaliteral,keyofequality with the zod input, and the three members (ObjectGridSchema,ListViewSchema,NamedListView) carrying the one rule.gridRowDecorationMembers-8071.test.tsx(plugin-grid): the stored native rule refused byObjectGridSchemaatfieldwith the retirement, AND painted by the real grid on the matching row only; the respelling accepted and painting the same row the same colour as the control.ListView.storedRuleDialect-11533.test.tsx(plugin-list, new): a stored list view's native rules refused byListViewSchemaatfield, AND handed to itsobject-gridnode untouched; the respelling as the control.apps/console/.../registry-inputs-spec-parity.test.ts: theobject-grid.conditionalFormattingmember-pin prose says the retired members are stored-rule reads now (prose only, no logic).Pending changesets (read, not recalled)
Read as text: every pending entry naming
conditionalFormatting,ConditionalFormattingRule, the resolver, a rule union, "broader than spec", or a file this diff touches (41 by symbol and prose, and the 51 thatcheck:changeset-claimslists by file name, each grepped for the symbols this diff moves).10946-expression-wire-slots-by-reference.md. Its "the list view's and the kanban board's rule unions share oneconditionschema" now describes no union at all; the note keeps every condition claim, which still holds and is still pinned. (+16/−0)11522-kanban-rule-dialect-retired.md. Its "Not changed" paragraph gives "because the grid's and the list view's rule union still declares them" as the reason the evaluator keeps every arm; the reason is now the stored-rule compatibility read. (+15/−0)7664-kanban-arm-plugin-dialect.md(kanban only, already noted by objectui#11522);6152-objectgrid-round6-mirrored.md(says the mirror declares the key typed as the twin types it, still true, and makes no dialect claim);6152-unmirrored-round3.md(the report's rule);11276-object-grid-properties-bag.md(the bag, which did not move);10976,10885,10758,7928(relays and named views, unchanged);10689×2,7216,7727,8167,8972,9318,row-predicate-phase2-record-only-5741(harvest and condition scope, unchanged);member-pins-object-grid-row-layer-8071(names the key only);8313,8801,9242,8932-retire-kanban-enhanced,7742,7322,4730,10275,8818(kanban, i18n or FLS readings that name the key only);4535and6956(nameexport-options-spec-parity.test.tsfor its export-options rows, not the neighbour control); the entries namingKanbanImpl.tsx(card, lane and click behaviour, none about the evaluator comment);10719(a different "rule union").check:changeset-overwritereports the two modified entries with their declarations unchanged, which is the appended-note case.Verification
ebb37b7b. It differs fromaab1cc53(where the suites below ran) by one test file,ListView.storedRuleDialect-11533.test.tsx(as anytoas never), re-run atebb37b7bwith plugin-list's type-check.pnpm exec vitest run --maxWorkers=2 packages/types/ packages/plugin-grid/ataab1cc53: 527 files, 10,923 tests passed (VERDICT command-exit 0).aab1cc53(the plugin-list, plugin-view, plugin-kanban, app-shell, core, components, react, i18n, console and schema-catalog files that read the member or the changed files): 968 tests passed. 19scripts/__tests__suites that read the changed files (one-authority, doc-links, doc-snippet, readme-exports, changeset gates and others): 1,093 tests passed.type-check(each script name echoed, each exit read separately), afterturbo run buildof the dependency closure:@object-ui/types(src, examples and test configs;--listFilesOnlyconfirms the new pin file is compiled),plugin-grid,plugin-list(again atebb37b7b),plugin-view,plugin-kanban: exit 0.HEAD: a native rule appended to plugin-grid'sspecExpressionWire-10946.test.tsxas anObjectGridSchemaliteral turned plugin-grid's test type-check red (exit 2, TS2322 atfield,operator,value), proving it reads the rebuilt declaration; after restore the blob equalsHEAD's andgit diff HEADis empty.check:doc-snippets(777 of 777 blocks, 0 failed),check:doc-examples(exit 0, ledger-exact),check:doc-types(exit 0). With the console built:check:sdui-registration-pinsexit 0,check:component-surface-parity(report-only) exit 0 with noobject-gridrow.ebb37b7b, all exit 0:check:new-line-citations(0 new),check:control-bytes,check-changeset-no-major,check-changeset-presence,check-changeset-fixed,check:changeset-claims,check:pending-changeset-literals,check:test-path-roots,check-type-check-coverage,check-doc-links,check:spec-symbols,check:doc-fences,check-doc-expression-carriage,check:readme-exports,check-changeset-overwrite;check-governed-queue-guard --testover the 18 paths: NOT GOVERNED.Acceptance notes
object-gridbag. At the installed spec the bag still accepts any rule, because the spec row's member isunknown; objectstackmain(4916168) types it as the list view's own member by reference, so the bag narrows with the@objectstack/*bump that carries objectstack PR #21463. Carrier: objectui#11438 (the 17.6.0 trunk) or the bump after it.skills/objectui/guides/schema-expressions.md's "Legacy shapes are translated" bullet says the native andexpressionforms "still work" under a heading naming list, grid and kanban: true of the evaluator, now false of authoring on all three carriers.skills/**is governed and this PR stays ungoverned, so it is left; it is the same item as objectui#11522's contract record escalation (c).ReportComponentSchema.conditionalFormatting, a{ field, operator, value, backgroundColor?, textColor? }shapeReportViewerreads per cell) is a separate declaration, and neither spec line declares a report rule member. Untouched. Carrier: none.ROADMAP.mdrecords the dual-format union as delivered; it is a dated log and is left.packages/plugin-kanban/src/KanbanImpl.tsx(comment only:getCardStyles' stated reason became false),ListView.storedRuleDialect-11533.test.tsx(the H3 pin),objectViewHostSurface.test.tsx(sentinel respelled), the console pin prose.Session:
https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPCGenerated by Claude Code