Repository navigation
feat(console): draw the map settings specifier as a table of keyed rows - #31451
Merged
Merged
Conversation
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jUhVXEJm46XTxRs4GM
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jUhVXEJm46XTxRs4GM
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jUhVXEJm46XTxRs4GM
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jUhVXEJm46XTxRs4GM
Contributor
Author
|
domain:ui#3 · zhuangjianguo · session_01QsR2jVEjiYrK7onirxUGyo Contract reviewServed-tier: The lane seat's own review of record, owed by the ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
Contributor
Author
|
domain:ui#3 · zhuangjianguo · session_01QsR2jVEjiYrK7onirxUGyo 席位核对 · ACCEPT · 2026-11-08T08:54Z · head
|
8 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.
Requested by Steedos · project thread
Fixes #31391
Clause-②: yes
Metadata-protocol: none
维护者速读
90d这样的字面量;配额列存成数字。保存时发送对象,删光所有行发送null。保存被拒时,错误指向的那一行(和那一格)会被标红,旁边是服务器的原句。环境变量锁定时整张表只读。map类型(服务端已在 feat(spec): a map settings specifier; the lifecycle retention overrides and quotas use it #31386 落地),控制台还没有对应的控件,管理员只能走 API 或OS_LIFECYCLE_*来设。console.settingsView.map*词条,十一个语言包都有。回滚即 revert。ui/apps/console/src/pages/settings/SettingsMapField.tsx(行的本地草稿、rowsToValue删光发null、时长单元格);settingsFieldRefusals.ts的mapRefusalPointers(只读constraint.entry/constraint.column);SettingsField.tsx的case 'map'。cd ui && pnpm exec vitest run apps/console/src/pages/settings/__tests__/SettingsView.mapRows-31391.test.tsx,预期 13 个用例全绿。What changes
SettingsMapField.tsx(new). The control fortype: 'map'. It renders a table: thekeyColumnheader, then the declaredcolumnsin order. Each stored entry is one row, in stored order. The rows are a local draft with stable ids, rebuilt fromvalueonly when the page hands back a value these rows did not produce (a reset, a discard, the re-read after a save).optionsthat no row uses yet, so a row is never keyless and Save never drops a half-made row. Each row's key picker offers its own key plus the unused ones. A stored key the servedoptionsno longer hold stays on its row, with a "Not offered" badge and a hint saying Save will refuse it. When nooptionsare served at all, the save door admits every key, so nothing is marked.durationcell is a whole-number box plus a unit select (h,d,w,y; a new cell defaults to days), stored as the literal, so 90 days is'90d'. Apositive_integercell is a number box whose value is sent as a number. Cells are sent as typed: a value the server would refuse is still sent, so the refusal names it; nothing is quietly repaired or dropped. An emptied cell is left out of a several-column entry.null, a clear, never{}.errorAtis the save door'sconstraint.entry/constraint.column. The row named byentryis marked (data-invalid, a tinted background). The cell named bycolumngetsaria-invalidandaria-describedbypointing at the error line. When the miss is the key or the entry itself, the row's key picker gets them instead. The sentence stays the row's error line, as the server sent it.SettingsField.tsx. Acase 'map'draws the control through the sharedwrapper, named as a group by the row label, and takes the newerrorAtprop. Thedefaultunsupported-type arm stays for every other unknown type (item 8).settingsFieldRefusals.ts.extractFieldErrorskeeps onlyfieldandmessage, somapRefusalPointersreadsconstraintfrom the samedetails.fieldsarray the code is read from. Onlyentryandcolumnare taken, and only whenconstraint.type === 'map'.allowed(one comma-joined string on the wire) andkindstay in the sentence. Aninvalid_typerefusal carries noentry, so it points at no row.SettingsView.tsx. The per-field error state carries{ message, at }, so the pointer reaches the row. A pre-save block still carries a message only.types.ts. The console mirror gains'map',keyColumnandcolumns(withkind: 'duration' | 'positive_integer'), frompackages/spec/src/system/settings-manifest.zod.ts.@object-ui/i18n. 11 newconsole.settingsView.map*keys in every locale pack:mapAddRow,mapRemoveRow(withkey),mapKeyNotOffered,mapKeyNotOfferedHint,mapNoRows,mapSearchKey,mapUnitLabel(withcolumn), andmapUnitHours/Days/Weeks/Years.zh-Hant.tsis regenerated fromzh-Hans.ts(gen:zh-hant). The empty-search text reusescommon.noResults.Tests
ui/apps/console/src/pages/settings/__tests__/SettingsView.mapRows-31391.test.tsxhas 13 cases over the realSettingsView→SettingsFieldrender path. Its fixtures are thelifecycleSettingsManifestshapes, with options and refusals as the door testkit measured them onmain:optionsstays, marked "Not offered".{ retention_overrides: { sys_audit_log: { maxAge: '90d' } } }.{ quotas: null }, asserted not to equal{}.invalid_optionanswer marks the row named byentryand its key picker, andaria-describedbypoints at the server sentence. Its fixture carriesallowedas the door's comma-joined string. Aninvalid_shapeanswer withcolumnmarks that cell and not the key. Editing clears the mark.Ablations (committed fix first, mutated through
scripts/ablation-replace.mjs, restored blob == HEADb56fdaae6ae9,git diff HEADempty):Number(typed)instead of the literal. The90dcase goes red:- "maxAge": "90d",+ "maxAge": 90.const unused = served). The picker case goes red:expected [ 'Audit log', 'Job run' ] to deeply equal [ 'Job run' ].Acceptance notes
packages/specchange on this card). The headers renderkeyColumn.labelandcolumns[].labelas served, throughpickLocalizedfor an inline locale map. Today they are English literals inlifecycle-settings.ts(Object,Keep rows for,Expire rows after,Max rows,Lifecycle class), so a zh page shows them in English beside translated labels. Translating them needs acolumnsgroup inpackages/spec/src/system/translation.zod.ts. The seat files that follow-up.quota_defaultsoption labels are the class names (audit,telemetry, …), aslifecycleClassKeySourceserves them. They go through the samelabels.optionLabelslot as every option, so a namespace bundle can translate them; none does today.jsonsetting saved from the console is stored as a string, so retention overrides and row quotas silently do nothing #31241 one-save heal (storedJsonTextDraft) seedsjsonkeys only, and these three keys aremapnow. This is a reading of the code, not a reproduction: no deployment holding such a value was measured.optionsand the refusal shapes come from the door testkit (packages/services/service-settings/src/settings-map-shape-30208.test.ts, 16/16 onmaincd6618e8), and the fixtures copy them.Clause-②: yesis the claim's line, copied. Measured on the builtui/packages/i18n/dist/locales/en.d.ts: every new key is a readonly literal leaf (readonly mapAddRow: "Add a row";), so the publishedTranslationKeyswidens. That is the(widening)arm, which the claim's bareyesleaves unstated.Verification
Derived gate union (dispatch-gates --commands): 38 of 38 exit 0
38 of 38 exit 0 at
bdc8319d(main3d73c1d6merged;dispatch-gates --ran: 38 derived, 38 run, 0 NOT-MEASURED).pnpm --dir ui exec vitest run scripts/ran atba2bb379(170 files passed, 2 skipped); the merge after it touched noui/path.node scripts/check-adr-0087-registration.mjs --base origin/main· exit 0node scripts/check-adr-0087-registration.mjs --self-test· exit 0node scripts/check-changeset-no-major.mjs --base origin/main· exit 0node scripts/check-closing-keyword-parity.mjs· exit 0node scripts/check-closing-keyword-parity.mjs --self-test· exit 0node scripts/check-comment-mask-corpus.mjs· exit 0node scripts/check-empty-changeset.mjs --base origin/main· exit 0node scripts/check-empty-changeset.mjs --self-test· exit 0node scripts/check-sdui-manifest.mjs· exit 0node scripts/check-sdui-manifest.mjs --self-test· exit 0node scripts/ci/snapshot-next.mjs --self-test· exit 0node scripts/pm/release-rehearsal-clone.mjs --self-test· exit 0node scripts/release-pending-publish.mjs --self-test· exit 0pnpm --filter @objectstack/spec run check:duration-unit-keys· exit 0pnpm check:changeset-gate-self-tests· exit 0pnpm check:closed-ceremony-text· exit 0pnpm check:cross-package-test-inputs· exit 0pnpm check:dispatcher-error-vocabulary· exit 0pnpm check:driver-memory-census· exit 0pnpm check:engine-double-contract· exit 0pnpm check:gitlink-declared· exit 0pnpm check:merge-driver· exit 0pnpm check:nul-bytes· exit 0pnpm check:published-files· exit 0pnpm check:query-options-erasure· exit 0pnpm check:refd-timer-probe· exit 0pnpm check:registry-log-declared· exit 0pnpm check:rest-log-spy-declared· exit 0pnpm check:role-word· exit 0pnpm check:skill-identifier-liveness· exit 0pnpm check:test-source-alias· exit 0pnpm check:tier-file-adoption· exit 0pnpm check:type-check-coverage· exit 0pnpm check:undeclared-dep-imports· exit 0pnpm check:watch-hint-literal· exit 0pnpm check:where-matcher· exit 0pnpm check:type-check-debt· exit 0pnpm --dir ui exec vitest run scripts/· exit 0Suites and type-check
turbo run type-check --filter=@object-ui/console --filter=@object-ui/i18n: 74/74 tasks, console and i18n both cache miss, executed.vitest run apps/console/src/pages/settings/: 40 files, 343 tests passed.vitest run packages/i18n/: 99 files, 1804 passed, 13 skipped (aftergen:zh-hant; before it,zh-hant-generated.test.tswas the one red).node scripts/check-i18n-en-drift.mjs(ui): no en value changed in range.eslinton the six touched console files: 0 errors, 6 pre-existing warnings.maincd6618e8:settings-map-shape-30208.test.ts16/16.Generated by Claude Code