Repository navigation
feat(ui): record-mode currency — each record shows its own currency; ISO 4217 code select pre-filled with the organization currency; no cross-currency totals; Studio currency section - #27340
Merged
Conversation
…unt points at a sibling ISO 4217 code field Claude-Session: https://claude.ai/code/session_01Utp6CbfVSc1hXtxLUPHZ2f Co-authored-by: Claude <noreply@anthropic.com>
…ent in the dropped-refinements ledger Claude-Session: https://claude.ai/code/session_01Utp6CbfVSc1hXtxLUPHZ2f Co-authored-by: Claude <noreply@anthropic.com>
…mode sites Claude-Session: https://claude.ai/code/session_01Utp6CbfVSc1hXtxLUPHZ2f Co-authored-by: Claude <noreply@anthropic.com>
…field naming convention, paid by tightening the currency block Claude-Session: https://claude.ai/code/session_01Utp6CbfVSc1hXtxLUPHZ2f Co-authored-by: Claude <noreply@anthropic.com>
…cord-mode currencyField Claude-Session: https://claude.ai/code/session_01Utp6CbfVSc1hXtxLUPHZ2f Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CbfVSc1hXtxLUPHZ2f Co-authored-by: Claude <noreply@anthropic.com>
…d; cells, currency widget and create-form seeding follow it Work in progress for the record currency display (ADR-0144 D2, D8 rule 4). Claude-Session: https://claude.ai/code/session_01Utp6CXtadYa5QXb2PbXPev Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CbfVSc1hXtxLUPHZ2f Co-authored-by: Claude <noreply@anthropic.com>
…er/group aggregates, dashboard/detail/gantt record reads Claude-Session: https://claude.ai/code/session_01Utp6CXtadYa5QXb2PbXPev Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CXtadYa5QXb2PbXPev Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CXtadYa5QXb2PbXPev Co-authored-by: Claude <noreply@anthropic.com>
…e field-level answer never reads a code from a record-mode config Claude-Session: https://claude.ai/code/session_01Utp6CbfVSc1hXtxLUPHZ2f Co-authored-by: Claude <noreply@anthropic.com>
…urrencyField row The field form's new `currencyConfig.currencyField` row is one more unwalked composite child (10 -> 11) and one more translated row label in each locale's positive control (695 -> 696). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CbfVSc1hXtxLUPHZ2f
… default Under `currencyMode: 'record'` each record names its own code in the sibling field, which only a per-row renderer can read. The field-level answer is now `undefined` outright, so a record with a blank code shows no symbol instead of a currency it never named. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CbfVSc1hXtxLUPHZ2f
…A/A) The code field's "Available" group now carries the codes the object's records hold: the form renderer reads them once per object with one grouped header query and hands them down on the field metadata (`usedCurrencyCodes`). The ISO 4217 picker moves to `@object-ui/components` as `CurrencyCodePicker`, shared with the filter. A list filter row on a record-mode amount gains a required currency picker defaulting to the organization currency; the builder emits the code condition as a companion row, so the group lowers to `amount op X AND code = C`. A paired group stays on AND. Claude-Session: https://claude.ai/code/session_01Utp6CXtadYa5QXb2PbXPev Co-authored-by: Claude <noreply@anthropic.com>
Co-authored trailers below; also tightens the plugin-list test's literal type. Claude-Session: https://claude.ai/code/session_01Utp6CXtadYa5QXb2PbXPev Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Oct 29, 2026
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CbfVSc1hXtxLUPHZ2f
The authorable-surface baseline is taken from main (#27233 retired `CurrencyValue`) and rebuilt, keeping `currencyConfig.currencyField`; the field reference page is regenerated from the merged tree. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CbfVSc1hXtxLUPHZ2f
This was referenced Oct 29, 2026
…-currency display branch Bring-in order. Two conflicts: - ui/packages/i18n/src/currency.ts: #27268 made record mode resolve to undefined with no tenant fallback; this branch reads the record's code when the caller passes the row. Kept the record read (a call with no record still resolves to undefined, which #27268's tests pin) and #27268's doc text. - ObjectFieldInspector.tsx: both import blocks kept. Claude-Session: https://claude.ai/code/session_01Utp6CXtadYa5QXb2PbXPev Co-authored-by: Claude <noreply@anthropic.com>
…ency (#27188) A grouped analytics answer over a `record`-mode amount marks the measure column with `currencyField` (#27338), naming the result column that holds each row's ISO 4217 code. `measureCellCurrency(field, row)` reads it structurally (the spec type does not carry it yet) and returns that row's code, nothing for a row without one, and the column's declared `currency` for every other column. The dashboard table's body cells and the Studio dataset preview read it off the RAW row, so a relabelled dimension cannot move a code. Totals and cross-tab cells pass no row and draw no symbol. Claude-Session: https://claude.ai/code/session_01Utp6CXtadYa5QXb2PbXPev Co-authored-by: Claude <noreply@anthropic.com>
… its row's currency (#27188) The grouped report table and its export snapshot now format each measure cell through `measureCellCurrency`, reading the code off the RAW row (`state.rows[i]`), as the dashboard table and the Studio preview do. The grand total passes no row and draws no symbol on a record-mode column. Every other column formats as before. Claude-Session: https://claude.ai/code/session_01Utp6CXtadYa5QXb2PbXPev Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Oct 29, 2026
…-irhbgx # Conflicts: # ui/packages/i18n/src/currency.ts # ui/packages/plugin-form/src/SplitForm.tsx
…7188) The merge of origin/main kept this branch's side of `authorable-surface/data.json`, which still recorded the DataEngine RPC request keys main retired (#27028). `gen:schema` on the merged tree drops them; the result is byte-identical to main's file. Claude-Session: https://claude.ai/code/session_01Utp6CXtadYa5QXb2PbXPev Co-authored-by: Claude <noreply@anthropic.com>
) `measureCellCurrency` had landed between `formatDimensionValue`'s JSDoc and the function, orphaning the docblock. Move the helper (and its own docblock) above it. No code change. Claude-Session: https://claude.ai/code/session_01Utp6CXtadYa5QXb2PbXPev Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Oct 29, 2026
Contributor
Author
|
epic:#8345#1 · os-bill · session_01Utp6CbTZrFqRYNgjSaSbN5 席位记录 · epic PM
|
This was referenced Oct 29, 2026
os-bill
pushed a commit
that referenced
this pull request
Oct 29, 2026
…-ufaqzz Brings in #27340 (ObjectFieldInspector, studio i18n) before the PR opens; no conflicts. Claude-Session: https://claude.ai/code/session_01Utp6CbCrKUjGeTReNEmUJx Co-authored-by: Claude <noreply@anthropic.com>
9 tasks done
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.
epic:#8345#1 · os-bill · session_01Utp6CbTZrFqRYNgjSaSbN5
Requested by Bill · project thread
Fixes #27188
⛔ not released alone — R1: pairs with #27184, #27186, #27187, #27188 (one Version Packages release)
Clause-②: no
Metadata-protocol: none
维护者速读
currencyMode: 'record'的字段和声明 ISO 4217 值域的文本字段;dynamic / fixed 字段行为不变(原有测试全过)。回滚即 revert 本 PR。ui/packages/i18n/src/currency.ts(解析器,record 分支)、ui/packages/components/src/custom/filterRecordCurrency.ts(筛选配对)、ui/packages/components/src/renderers/form/usedCurrencyCodes.ts(已用币种查询)、ui/packages/plugin-grid/src/recordCurrencyAggregates.ts(合计规则)。cd ui && pnpm exec vitest run packages/fields/src/__tests__/recordCurrency.faces-27188.test.tsx,应见三行 $ / € / ¥,无币种的行显示纯数字。currencyMode: 'record'+currencyField— a record-mode amount points at a sibling ISO 4217 code field (multi-currency M0-1) #27268 已合并)。What changes
ADR-0144 D2 (the record currency lives in a sibling code field), D6 (raw aggregates over a record-mode amount stand only inside one currency) and D8 rule 4 (the form pre-fills the organization currency; the stored value is always explicit), on the renderer side.
@object-ui/i18n).resolveFieldCurrency(field, tenantDefault, record?)gains an optional record. UndercurrencyMode: 'record'it reads onlyrecord[currencyConfig.currencyField]; no record or a blank code resolves toundefined, so the face shows a plain number. It never falls back to the tenant default, a node-levelcurrencyor a straydefaultCurrency.dynamic/fixedare unchanged. NewrecordCurrencyField(field)names the pointer;FieldCurrencyConfigtypes the three modes.CurrencyCellRendererand the formula cell (the grid already handsdependentValues={row}),CurrencyField(symbol, minor-unit decimals and spinner step follow the selected code), the form renderer (hands the live record to thecurrencyandformulawidgets,RECORD_CURRENCY_WIDGET_TYPES), the grid's mobile card, the record page's summary chips, the dashboard table cell and record drawer (FieldMeta.currencyConfig, copied only for record mode), and Gantt tooltips.textfield declaringvalueDomain: 'iso_4217_currency'rendersCurrencyCodeField: a searchable select grouped "Available" (home currency, then rated, then the codes in use) and "All currencies (ISO 4217)", offering only codesisValueDomainMember('iso_4217_currency', code)accepts. Re-selecting the selected code keeps it (no click-to-clear). The picker isCurrencyCodePickerin@object-ui/components, shared with the filter row below.@object-ui/componentsform renderer,usedCurrencyCodes.ts). For every ISO 4217 code field on a form with anobjectName, the renderer asksdataSource.queryGroupHeaders(object, compileListViewGroupQuery(grouped by the code field))once, keyed on content, and hands the distinct stored codes down on the field's own metadata asusedCurrencyCodes. No widget-contract key. All five plugin-form hosts already putobjectNameon the form schema, so this one seam covers them. A data source withoutqueryGroupHeadersis not asked (aggregatemay degrade to a full fetch); a failed read hands nothing down. Either way the picker offers home + rated + the record's own code.@object-ui/componentsFilterBuilder,@object-ui/plugin-list). A builder field entry may carrycurrencyField(the list sets it fromcurrencyConfigfor record-mode amounts). A value-comparing row on such an amount draws a required currency picker, defaulting tolocalization.currency. The builder emits the code condition as a companion row of the same flat group,{ id: '<row id>::currency', field: <code field>, operator: 'equals', value: C }, so the group lowers toamount op X AND code = Ceverywhere it is lowered (convertFilterGroupToASTgives['and', ['amount','>',100], ['currency_code','=','USD']]) and stored filters keep theViewFilterRuleshape. The companion is drawn as the picker, never as a row, and goes when its amount row goes, switches to a non-pairing field, or switches to a presence operator. A paired group cannot be toggled to OR, because under OR the pair would split; a stored OR group shows why. Opening a stored filter emits nothing (a stored amount row without a code shows a blank, invalid picker until the next edit). The toolbar's filter count does not count companions.@object-ui/plugin-form).SeedContext.homeCurrency: a code field that some record-mode amount points at, with no declared default, opens onlocalization.currency. Wired into ObjectForm, DrawerForm, ModalForm, TabbedForm and WizardForm (SplitFormis retired onmain, ui(plugin-form):ObjectFormdrops thesplit/drawer/modalbranches andSplitFormretires; showcasetaskviews re-spelled (ADR-0050 cutover, renderer half of #27155) #27159). Edit forms are not seeded, so opening a record never rewrites its stored code.@object-ui/plugin-grid). The footer draws no sum / avg / min / max for a record-mode amount; counts stand. Group-header aggregations keep those only when the grid is grouped by the amount's code field (honestGroupAggregations, applied before both the server header query and client bucketing). The home field is adynamicamount and totals as before.@object-ui/app-shell). A currency field, or a formula returningcurrency, gets a currency section (CurrencyConfigField): mode, the fixed code, and underrecordacurrencyFieldpicker listing only the object'stext+ ISO 4217 fields, with "Create currency field", which appendscurrency_code(type: 'text',valueDomain: 'iso_4217_currency',required: true) and sets the pointer in one write. A mode switch keeps only the keys that mode reads.@objectstack/types,@object-ui/console; R1 rider from feat(spec):currencyMode: 'record'+currencyField— a record-mode amount points at a sibling ISO 4217 code field (multi-currency M0-1) #27268's contract review). The resolve answer's field projection (buildShareLinkResolveAnswer) carried onlycurrencyModeanddefaultCurrency, so a shared record-mode amount rendered as a plain number. A currency field's projectedcurrencyConfignow also carriescurrencyField, only when the answer'srecordcarries that code field, so a link that redacts the code field discloses neither the code nor the field's name and the amount shows as a plain number. The shared-record page passes the shared record to the currency face asdependentValues, so the amount shows in its own record's currency.@object-ui/core,@object-ui/plugin-dashboard,@object-ui/plugin-report,@object-ui/app-shell; R1 rider from feat(spec,service-analytics): record-currency analytics — grouped answers name a per-row currency, the cube door refuses an ungrouped amount aggregate, compile guards on measure currencies #27338's contract review). A grouped analytics answer over arecord-mode amount marks its measure column withAnalyticsResult.fields[].currencyField, naming the result column that holds each row's code (feat(spec,service-analytics): record-currency analytics — grouped answers name a per-row currency, the cube door refuses an ungrouped amount aggregate, compile guards on measure currencies #27338). The readers passed onlyfields[].currency, so those columns rendered as plain numbers. NewmeasureCellCurrency(field, row)returns that row's code when the column carriescurrencyField, nothing for a row without one, and the column's declaredcurrencyotherwise, so every other column formats byte for byte as before. It readscurrencyFieldstructurally ((field as { currencyField?: unknown }).currencyField): feat(spec,service-analytics): record-currency analytics — grouped answers name a per-row currency, the cube door refuses an ungrouped amount aggregate, compile guards on measure currencies #27338's contract is not onmainyet, so the spec type does not carry the key. That passes type-check, and it can switch to the spec type once feat(spec,service-analytics): record-currency analytics — grouped answers name a per-row currency, the cube door refuses an ungrouped amount aggregate, compile guards on measure currencies #27338 lands. The dashboardtablewidget's body cells, the report table's body cells and its export snapshot, and the Studio dataset preview read it off the RAW row (state.rows[i], beforerelabelDimensions). Totals and cross-tab cells pass no row and so draw no symbol, since they may add several currencies.chart-series.tsreads no currency at all (only the numeralformat), so it has nothing to switch, and a chart axis has one unit.fields.currencyCode.*in all eleven locale packs;designer.field.currency.*in the studio namespace (en, zh).File surface beyond the Claim's list, accepted by the epic PM:
ui/packages/plugin-grid,ui/packages/components; alsoui/packages/plugin-form,ui/packages/plugin-detail,ui/packages/plugin-gantt(one resolver call each, plus the form seeding), andui/packages/plugin-list(the list filter'scurrencyFieldand filter count).Tests
i18n/src/__tests__/resolveFieldCurrency.recordMode-27188.test.ts— the record-mode truth table.fields/src/__tests__/recordCurrency.faces-27188.test.tsx— cells in USD / EUR / JPY, no-code rows plain, the amount box's symbol and step (USD 0.01, JPY 1, KWD 0.001), the code select's groups and domain filter,TextFieldrouting.plugin-form/src/__tests__/recordCurrencySeed-27188.test.ts— seeding, unset setting, author default and caller record win.plugin-grid/src/__tests__/recordCurrencyAggregates-27188.test.tsx— footer refuses money aggregates over record mode, counts stand, home field sums; group aggregations by grouping field.plugin-dashboard/src/__tests__/recordFields.recordCurrency-27188.test.tsx— dashboard cells per row.app-shell/.../CurrencyConfigField.recordMode-27188.test.tsx— mode switch, candidates, and the create shortcut's output parses underObjectSchema.components/src/renderers/form/__tests__/form-used-currency-codes-27188.test.tsx— the query shape, the distinct domain-valid read, one ask per object across re-renders, no ask without an object or the capability, a failed read.components/src/__tests__/filter-builder-record-currency-27188.test.tsx— the pairing rules, one picker per paired row, a new row on the home currency, removal, the stored blank picker emitting nothing, the AND lock and the OR notice.plugin-list/src/__tests__/convertFilterGroupToAST.recordCurrency-27188.test.ts— the pair lowers toamount op X AND code = C.packages/types/src/share-link-resolve-answer.test.tsgains: the pointer is projected when the record carries the code field, and a redacted code field is never named.console/src/pages/SharedRecordPage.recordCurrency-27188.test.tsx— a shared record-mode amount shows its record's currency (EUR, then JPY), and a hidden code field leaves a plain number.core/src/utils/__tests__/dataset-format.recordCurrency-27188.test.ts—measureCellCurrency: per-row code, no code / no row gives no symbol, a column withoutcurrencyFieldkeepscurrency; two rows of one column format as$1,234.50and€1,234.50.app-shell/.../previews/DatasetPreview.measureLocale.test.tsxgains: a record-mode column renders$1,234.50and€99.00in one table.plugin-dashboard/src/__tests__/DatasetWidget.recordCurrency-27188.test.tsx— the table widget formats USD, EUR and a code-less row (plain), and the grand total has no symbol.plugin-report/src/__tests__/DatasetReportRenderer.measureLocale.test.tsxgains: the grouped report table formats USD, EUR and a code-less row (plain), and the grand total has no symbol.fields/.../recordCurrency.faces-27188.test.tsxgains: the used codes offfield.usedCurrencyCodesappear in "Available" after the home currency.Acceptance notes
amount op X AND code = C. Both are built here.ratedCurrencyCodes()inCurrencyCodeField.tsxreturns nothing untilsys_exchange_rateships (R2, platform-objects:sys_exchange_rate{ currency, rate, effective_date, source } — tenant-scoped, admin-only, audited; Setup list view (multi-currency M1-1, epic #8345; released with M2) #27185).objectName, rather than five host-level copies. It is a per-mount read: a code first used after the form opened shows up on the next open.FilterBuilder+ListView). The sharing-rule criteria editor (FilterConditionField, stored as a Mongo-style object) is not paired: it re-ids rows on every read, so a code condition there stays a row the author writes.localization.currencyunset a new pair's code is blank and the picker is marked invalid; the list's lowering drops a blank row, so that filter compares the amount alone until a code is picked.currencyDisplayWidth/currencyFractionDigitsreader (CLDR viaIntl), whichCURRENCY_FRACTION_DIGITSmirrors by construction (its header); no second table.chart-series.tsreads no currency at all (only the numeralformat), and a value axis has one unit, so per-row currency there is a follow-on, not part of R1's table surfaces.homeAmount) is ui: home field read-only with "converted at RATE on DATE"; live preview when a rate exists; Studio editor forhomeAmount(multi-currency M3-2, epic #8345; the read-only display is released with M2) #27196's.Verification
Head
554bd578ona9b2b97d, which regeneratespackages/spec/authorable-surface/data.jsonon the merged tree (the result is byte-identical tomain's), on24055cf0, a merge commit bringing inmainatf7188b56after #27268 landed as41452103(no rebase). The diff againstmainis this card's own: 70 files, none underpackages/spec. Two conflicts:ui/packages/i18n/src/currency.ts.main's side is feat(spec):currencyMode: 'record'+currencyField— a record-mode amount points at a sibling ISO 4217 code field (multi-currency M0-1) #27268's final file, byte-identical to the one this branch had already reconciled, so this branch's side is kept. Underrecordmode the resolver reads the row's code when the caller passes the row; a call with no record still resolves toundefined, which feat(spec):currencyMode: 'record'+currencyField— a record-mode amount points at a sibling ISO 4217 code field (multi-currency M0-1) #27268's ownresolveFieldCurrency.test.tscases pin.ui/packages/plugin-form/src/SplitForm.tsx.mainretired it (ui(plugin-form):ObjectFormdrops thesplit/drawer/modalbranches andSplitFormretires; showcasetaskviews re-spelled (ADR-0050 cutover, renderer half of #27155) #27159), so the deletion stands, along with this card's seeding call in it.554bd578movesmeasureCellCurrencyaboveformatDimensionValue's docblock, which it had orphaned. It is a doc-only move.check:generated(spec) passes at this head. ADR-0144 citations (D2, D3, D6, D8 rules 3 and 4, D9) were re-read against the landeddocs/adr/0144-…mdand hold.Landing verification at
554bd578@objectstack/spec,@objectstack/types, and the ui closure of app-shell, console and plugin-report, exit 0.@objectstack/spec check:generatedexit 0.os-verify-lock:visible/showWhenontovisibleWhenon page:tabs items and screen fields #8415, dataset-format, dataset preview, dashboard table and report table files: 55 files, 855 passed;plugin-form/srcsuite: 210 files, 2343 passed, 1 skipped;core/srcsuite after the docblock move: 218 files, 4133 passed, 27 skipped;@object-ui/i18n, fields, components, plugin-form, plugin-grid, plugin-list, plugin-dashboard, plugin-detail, plugin-gantt, plugin-report, core, app-shell, console, and@objectstack/types.dispatch-gates --commandson this card's 70 paths derives no family the earlier runs had not. The 34 local re-runs exit 0; the rest are CI's.Targeted tests — 49 files, 786 tests, exit 0
Before the merge, the neighbouring-package run (i18n, components form renderer and custom, fields widgets, plugin-list filter suites) was 328 files, 3544 passed, 30 skipped.
Type-check and lint
pnpm --filter @object-ui/PKG type-checkexit 0 atad386034for i18n, fields, components, plugin-form, plugin-grid, plugin-list, plugin-dashboard, plugin-detail, plugin-gantt, app-shell, after building their dependency closure.pnpm exec eslintover every ui file this card touches: 0 errors.Ablations at
ad386034— three, each red then restoredui/packages/i18n/src/currency.ts:currencyMode === 'record'→'ABLATED'(blob82ae7ac6→c78a363b): 7 failed / 8 passed across the i18n record-mode suite and the fields faces suite.ui/packages/components/src/renderers/form/form.tsx:withUsedCurrencyCodes(field.field || field, …)→field.field || field(blob12d5c6b5→18cbb6d6): 1 failed / 6 passed.ui/packages/components/src/custom/filterRecordCurrency.ts:pairedCurrencyFieldanswersundefined(blob025b1560→d78defb9): 9 failed / 4 passed across the builder suite and the AST lowering.Each restored by
ablation-replace.mjs: blob == HEAD,git diff HEADempty.Share-link rider at
8bcaa1bc@objectstack/runtimeshare-links-field-projection.test.ts12 passed;@objectstack/typesshare-link-resolve-answer.test.ts9 passed; consoleSharedRecordPage.recordCurrency-27188,SharedRecordPage-27121,shared-record-shape22 passed. All exit 0 underos-verify-lock.@objectstack/typesand@object-ui/consoleexit 0. eslint over the five files: 0 errors.dependentValues={record}→dependentValues={undefined}inSharedRecordFields.tsx(blobe4ce54ff→88b99e73): 2 failed / 1 passed. Restored: blob == HEAD.currencyFieldspread →false ? … : {}inshare-link-resolve-answer.ts(blob8c7a3bd5→57379712): 1 failed / 8 passed. Restored: blob == HEAD.CI fixes at
66a421ccandc36aa49eCI's Test UI at
8bcaa1bcfailed one test:ui/packages/types/src/__tests__/filter-builder-condition-id-8415.test.tsreadsremoveCondition's id filter verbatim, and this card had widened it to also drop the companion. The line is back to the pinned form. The companion still goes with its amount row, becausehandleChangerunssyncRecordCurrencyRows, which drops a companion whose amount row is gone. The "removing the amount row removes its code condition" case covers this.visible/showWhenontovisibleWhenon page:tabs items and screen fields #8415 pin plus every components__tests__andcustom/__tests__suite, 255 files, 2431 passed, 7 skipped, exit 0 underos-verify-lock.@object-ui/componentsexit 0. eslint: 0 errors.CI's Test UI at
8bcaa1bcalso failedui/examples/schema-catalog/test/form-field-channel-census-12130.test.tsx, which measures every key the form-field channel reads by name. This card adds two reads: the form's used-codes hand-off (usedCurrencyCodes.ts) reads the entry'sfieldstash andvalueDomain, and the currency-code widget readsvalueDomainoff its carrier.usedCurrencyCodes.tsjoins thefieldrow's readers.valueDomaingets a RUNTIME-ONLY ledger row: it is object-field metadata (ADR-0144 D2) the code field takes from the object, so it is ledgered rather than declared onFormFieldSchema.os-verify-lock.Studio and #27268's record-mode refusals
#27268 refuses
defaultCurrencyundercurrencyMode: 'record', andcurrencyFieldhas.min(1).The Studio currency section never writes either refused shape.
switchCurrencyModedropsdefaultCurrencyon the switch torecord, the pointer picker deletes the key rather than writing'', and "Create currency field" writes a fresh{ currencyMode: 'record', currencyField }. A new test drives the whole path through the inspector: a fixed EUR amount, a switch to Record, then a picked pointer. It asserts the saved config and parses the saved object withObjectSchema.switchCurrencyModecarriesprev.defaultCurrencyintorecord(blobad4d29d0→8cd4f3de): 2 failed / 3 passed. Restored: blob == HEAD.visible/showWhenontovisibleWhenon page:tabs items and screen fields #8415 files, 51 files, 831 passed. Speccurrency-config-record-mode.test.ts: 15 passed. Share-link runtime, types and console: 12, 9 and 22 passed. All exit 0 underos-verify-lock.dispatch-gates --commandsnewly derives at this head (check:adr-anchors,bash32-floor,cli-command-ids,entry-guard,parse-guard,pnpm-filter-targets,pnpm-flag-spellings,scripts-symbol-anchors), exit 0 each.Dataset-answer rider at
7d062908measureCellCurrencyalways answers the column'scurrency(blob488643d0→da6fa0c1): 4 failed / 9 passed, one red case in each of the three files (core, preview, dashboard table). Restored: blob == HEAD.core/src/utils/__tests__/,app-shell/.../metadata-admin/previews/andplugin-dashboard/src/__tests__/: 433 files, 5275 passed, 6 skipped, exit 0 underos-verify-lock.@object-ui/core,@object-ui/plugin-dashboard,@object-ui/app-shellexit 0. eslint over the six files: 0 errors.dispatch-gates --commandsderives no new family at this head. The 34 local re-runs exit 0.Report table at
3e2eb1d0:fmtreadsmeasureField(measure)?.currencyinstead ofmeasureCellCurrency(measureField(measure), row)(blob8f61bf2d→631d266c): 1 failed / 10 passed. Restored: blob == HEAD.plugin-report/srcsuite, 30 files, 278 passed, 7 skipped, exit 0 underos-verify-lock.@object-ui/plugin-reportexit 0. eslint: 0 errors. No new gate family. The 34 local re-runs exit 0.Derived gates — 34 local runs, exit 0 each
Re-run locally at each head: the families
dispatch-gates --commandsmatches by this card's paths, pluscheck:nul-bytes,check:cross-package-test-inputs,check:doc-authoring,check:issue-citations,check:type-check-coverage,check:keyed-text-bounds,check:role-word,check:adr-anchors,check:bash32-floor,check:cli-command-ids,check:entry-guard,check:parse-guard,check:pnpm-filter-targets,check:pnpm-flag-spellings,check:scripts-symbol-anchors, and@objectstack/spec check:generated. The rest are CI's.🤖 Generated with Claude Code
https://claude.ai/code/session_01Utp6CXtadYa5QXb2PbXPev
Generated by Claude Code