Repository navigation
test(ui): assert what ui needs from spec-owned shapes, not their exact counts (#26238) - #26272
Merged
Merged
Conversation
…by key count (#26238) Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HRaa5qRyCQv9YZJCosen4d
…ions (#26238) Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HRaa5qRyCQv9YZJCosen4d
…sured 22 (#26238) Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HRaa5qRyCQv9YZJCosen4d
…-l7vqkg # Conflicts: # ui/packages/app-shell/src/services/MetadataService.retiredObjectEnabled.test.ts # ui/packages/app-shell/src/services/MetadataService.specKeyObjectPayload.test.ts
Contributor
Author
|
ACCEPT · seat 2 · 2026-10-26T18:12Z · PR #26272 head
Ruling on the open question: keep the derived ui-vs-spec parity guards red on an additive spec change. Their red names a ui gap someone must close (a missing label, consumer or declaration), which is the opposite of a brittle count; #26092 makes them run on the spec PR itself, so the spec author sees them before main does. Out-of-scope finding (51 type-level Seat's route: ready + auto-merge. |
This was referenced Oct 26, 2026
This was referenced Oct 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Steve · project thread
Fixes #26238
Clause-②: no
Metadata-protocol: none
What changed
Test-only. 38
ui/**test files pinned an exact count, or the whole value list or key list, of a schema, enum or constant that@objectstack/specowns. Each of those pins is now a membership assertion on what ui actually reads, writes or must refuse. Retirement guards name the retired key as absent and the successor as present. The two ObjectSchema corpus guards in app-shell also assert directly that no accepted key is within two edits of a retired or resolved key. That check replaces the key count, which unrelated spec adoptions had moved six times (42 → 48). Exact pins on ui-owned shapes are unchanged. No product source, nopackages/spec, no locale pack.An additive spec change (a new key, enum value or constant member) now leaves these 38 files green, unless ui really lacks something. The kept parity guards listed below are the cases where ui does lack something.
Sweep: patterns and control hits
Scanned at merge-base
df8aac41f: 1273ui/**test files that name@objectstack/spec, through static imports,await import,vi.importActualandrequire.df8aac41f)Object.keys(X.shape)or a derived variable, then.size/.lengthtoBe(N)/toHaveLength(N)app-shell/src/services/MetadataService.retiredObjectEnabled.test.ts:161(accept.size).toBe(47)).options/.def.entriesof a spec enum, thentoEqual([literal…])app-shell/src/utils/__tests__/recordFormModalSize-12803.test.ts:34Object.keys(X.shape).sort()thentoEqual([literal…])app-shell/src/views/ObjectView.kanbanLane-8193.test.tsx:227toHaveLength(N),.size/.length).toBe(N)) in a spec-reading file, triaged by hand (129 hits)plugin-designer/src/MetadataFieldsPage.storedTypePreserved.test.tsx:228(toHaveLength(49), FieldType)toEqual([literal…])or a count (a broad pass over every spec identifier, 228 hits, triaged by hand)app-shell/src/services/MetadataService.readDecorationStrip.test.ts:129(METADATA_READ_DECORATIONS)toMatchSnapshot/toMatchInlineSnapshotin a spec-reading filecomponents/src/__tests__/snapshot-critical.test.tsx, which is not spec-readingThe scanner was a scratch script. It recognised static named and namespace imports, destructured dynamic imports, and one level of derived variables (multi-line declarations too). Pins reached only through
@object-ui/typesre-exports of spec values are outside it. The ones met while triaging (ViewTypeSchema,TextSchema,VIEW_ITEM_KEYS) were read by hand.Converted (38 files)
Paths are relative to
ui/packages/unless they start withui/.app-shell/.../MetadataService.retiredObjectEnabled.test.ts:accept.size47 removed. Added: no near-spelling, within two edits, ofenabledor_deleted(exceptenable, which the next lines refuse by value). The six-entry re-point history is condensed to one line.app-shell/.../MetadataService.specKeyObjectPayload.test.ts:accept.size47 removed. Added: no near-spelling ofgroup,sortOrderorrelationships. History condensed, and the header's "43 keys" dropped.app-shell/.../recordFormModalSize-12803.test.ts: the wholemodalSizeenum → each tested size is an allowed value.app-shell/.../ObjectView.kanbanLane-8193.test.tsx: the 4-key KanbanConfig list →groupByFieldpresent;groupFieldandgroupByabsent.app-shell/.../plugin-runtime-tier-3846.test.tsx: the literal tier list → membership. The derivedRUNTIME_FALLBACKequality stays (see Kept).app-shell/.../packageSource-12790.test.ts: union branch count 2 → a non-empty floor.app-shell/.../ActionPreview.locations.test.tsx:ACTION_LOCATIONS.length6 → a floor.global_navstays absent.app-shell/.../MetadataService.readDecorationStrip.test.ts,data-objectstack/src/draft-envelope.test.ts: the decoration list → contains_diagnosticsand_draft.app-shell/.../FlowReferenceField.specDerivation.test.ts: the spec source's 3-key list → containsobject,sourceandvalueField, and none of the presentation keys this package owns.core/.../normalize-list-view.foldOutputAuthorable-5435.test.ts: count 11 and the key list → the seven toggles the fold emits are declared.core/.../normalize-list-view.pageResidual-8429.test.ts: counts 9/11/9/11 and both literal lists → the nine drawn kinds present;pageabsent; the objectui face is the spec's plus two; the census usestoMatchObjectplus the derived "every spec kind resolves to itself".core/.../filter-tokens.recordContext-7297.test.ts:['record_id']→ contains. The compile-time ratchet stays in the type check.core/.../filter-view-rule-arity-8557.test.ts: the literal array and scalar class lists → membership. The exact partition stays.data-objectstack/src/listViews.roundTripKeys-11013.test.ts: the 5-key list → contains each.data-objectstack/src/metadata-client.overlayScope.test.ts:['env']→ contains. The old spellings are still refused by name.plugin-dashboardandplugin-reportspec-symbol-dataset-totals-11073.test.ts:['groupings']→ contains.plugin-designer/src/MetadataFieldsPage.storedTypePreserved.test.tsx: FieldType 49, census 22 andchecked === 22→ the census holds every measured member, andchecked === CENSUS.length.DESIGNER_TYPES27 (ui-owned) stays.plugin-detail/.../recordActivityFeed.test.ts: declared 13 and the unproduced list → membership plus an exact partition. The produced set (ui-owned) stays exact.plugin-list/.../UserFilters.tabPresetOperators.test.tsx:VIEW_FILTER_OPERATORS.length20 → a floor plusicontains.plugin-tree/src/ObjectTree.schemaTyped-8655.test.ts,types/.../calendar-flat-color-allday-8466.test.ts,types/.../related-list-add-picker-filter-mirror-9964.test.ts: block key lists → the keys this code reads.sdui-parser/.../dashboard-widget-options-census.test.ts: the 5-key list → contains each. The derived consumed/declared parity stays (see Kept).auth/.../auth-spec-parity.test.ts: the 6-provider list → containsgithub,googleandlocal.fields/.../LocationField.optionalKeys.test.tsx: the 4-key list →lat,lngandaltitudepresent;latitudeandlongitudeabsent.fields/.../RegistryRefField.audienceAnchors-12200.test.tsx: the anchor list → containseveryoneandguest.ui/apps/console/.../component-input-union-specimens.test.ts: the literal accepted-arm lists →arrayContaining.defaultValuekeeps its no-object-arm claim.types/.../export-options-spec-parity.test.ts,types/.../object-grid-export-options-refusal-7762.test.ts: union arms 2 and the key count or list → a floor plus named keys.types/.../grid-kanban-calendar-members-round8-6152.test.ts: the operations key list → named members. The local face is derived as the row's keys plus the two tombstones.types/.../list-view-options-bag-6152.test.ts: the literalKINDSlist → contains this package's block kinds.types/.../record-highlights-fields-icon-9280.test.ts,types/.../record-highlights-layout-9187.test.ts: the entry, layout and props key lists → named members, withiconabsent.types/.../select-option-form-view-boundary-12736.test.ts,types/.../select-option-spec-extension-7014.test.ts: the SelectOption key lists → named members, withiconanddisabledabsent.types/.../object-view-unmirrored-keys-7779.test.ts: the 51-key list, count 51, the retired list and live count 43 → membership and a floor.Kept, with the reason
RUNTIME_FALLBACK= tiers;PACKAGE_SOURCES= sources; thepageResidual"no undrawable spec kind"; theUserFilters"every view operator is AST-valid"; thesdui-parserconsumed ⊇ declared;export-optionsLOCAL keys = spec keys;object-view-unmirrored-keys"narrower than the protocol is empty" (the maintainer principle stated in that file);report-chart-query-spec-parity:614;navigation-spec-parity:316;bulk-action-spec-parity:46;filter-operator-protocol-set-9559:105;mirror-groups-cd-10286:67;ObjectValidationsPanel.runsOn-11923:134;ObjectMetricWidget.countNotCurrency-10356:114;imported-defaults-8317:637. tooling: the root tsconfig.json is both the shared base for 72 packages and the scripts/ program (the one DEBT entry, 26 errors); 14 packages re-set module resolution three ways; 18 vitest configs are byte-identical; three debt ledgers are empty #26092 is the half that makes these visible on the spec PR itself.DESIGNER_TYPES27; produced feed kinds;OBJECTUIlocal categoriesdetail/list; themetadata-form-i18nheader children (the ui bundle's rows); the i18n locale and key counts; fixture and row counts (plugin-grid,plugin-map,deriveMasterDetail, …);offline-nav-performanceliteral arrays (typed against spec types, so tsc already checks membership); thetest-supportspec-tombstones fixture schema; theobject-view-unmirrored-keysbuckets over ui's own declared and unread members;object-view-slot-key-lists(ui's d.ts members).list-view-blocks-by-reference-6152:122-125): an additive optional key cannot move a required set. Adding a required member is a narrowing, so it is breaking, not additive.unrecognized_keysnaming a key, a refusal path, a parsed value): these already assert one named key or value.Proof: a scratch extra member in the spec, old assertion vs new
25 scratch members were added in one pass to the spec source: keys
zzScratchKeyand valueszz_scratch_*, across ObjectSchema, FormView.modalSize, KanbanConfig, UserActionsConfig, VIEW_FILTER_OPERATORS, TreeConfig, CalendarConfig, ObjectListView, the ListView type enum, ACTION_LOCATIONS, PluginRuntime, METADATA_READ_DECORATIONS, APPROVER_VALUE_SOURCES, RECORD_CONTEXT_TOKENS, DatasetTotals, FieldType, SelectOption, FeedItemType, DashboardWidgetOptions, AuthProvider, AUDIENCE_ANCHOR_POSITIONS, overlayScope, RecordHighlights (key and layout value), the export-options object branch and the location value. Then a full@objectstack/specbuild, the 38 files at this branch, the same 38 files atdf8aac41f(written to the tree only), and a restore. After the restore,git diff HEADwas empty on the spec, the rebuilt dist had 0 markers, and the ui tests matched HEAD.VALID_AST_OPERATORS: the scratch added it to one spec list only), sdui-parser (DatasetWidget does not consume the new key), export-options-spec-parity (the TS mirror lacks the new key) and object-view-unmirrored-keys (objectui does not declare the new live key). Every converted line in these files was green.VIEW_ITEM_KEYS, the form-view option, and element arms. Their conversions use the same membership shape.Verification
At head
cf00e778(after mergingorigin/mainwith #26231 in), with@objectstack/{spec,client,formula,lint}closures rebuilt, all underscripts/pm/os-verify-lock.sh:Test Files 38 passed (38) · Tests 891 passed (891), VERDICT command-exit 0.402e830c, the full suites of the 12 touched packages:auth core data-objectstack fields plugin-dashboard plugin-designer plugin-detail plugin-list plugin-report plugin-tree sdui-parser types. Result:Test Files 1790 passed | 2 skipped (1792) · Tests 28011 passed | 65 skipped, VERDICT command-exit 0, 43m51s.app-shell(about 1725 files) andui/apps/consoleran only their changed files. Their full suites are left to CITest Core/ Test UI.node scripts/pm/dispatch-gates.mjs --commandsderived 24 families, all 24 run, each exit 0 (check:type-check-debtunder the lock: VERDICT command-exit 0, 26 raw tsc errors, none above its recorded number).--ranreconciliation: 24 derived, 24 run, 0 NOT-MEASURED, 0 UNRUN.Acceptance notes
df8aac41fthere are 51Equaltype assertions against a literal union of names in 31 spec-reading ui test files, for exampleplugin-chatbot/src/__tests__/spec-symbol-batch6.test.ts:251(PendingActionStatus). Some are on ui-owned types. Those on spec-derived types redType Check UI, not Test UI, when the spec adds a member. Converting them needs a per-type ruling (exact type equality vsextends), so they are noted for the seat to card. They are not carried by this PR.filter-view-rule-arity-8557was a deliberate review trigger: a new spec operator lands in the value-requiring class by default. That default is now held only by the exact partition, not by a whole-vocabulary list.files[]shipsdistalone, so nothing publishes. Labelledskip-changesetat open.Generated by Claude Code