Skip to content

feat(console): clear the value saved beneath an env pin in Setup - #31039

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-1tgs4d
Nov 6, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-1tgs4d

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Fixes #30665

Clause-②: yes (widening)
Metadata-protocol: none

维护者速读

What changes

  • SettingsField.tsx: storedBeneathPin(resolved, writeScope) reads the server's cascadeChain alone: true when the row is env-pinned (locked, source: 'env') and an entry beneath the env entry at the scope this page writes carries a non-null value (a secret's entry is the read mask, which counts). On such a row, and only when the host passes onClearSaved, the row draws one line ("A value is also saved here. The environment value stays in force; ...") and a "Clear the saved value" button. The button opens a confirm (title and action "Clear the saved value", body saying the saved value will not come back when the variable is removed and the environment value stays in force; Cancel is the shared actionConfirm.cancel). Drawn under the control for every input row and under the help text for a toggle row.
  • SettingsView.tsx: onClearSaved(key) writes PUT /api/settings/:namespace with { [key]: null } for that key alone, merges the read-back, toasts "Saved value cleared" and reads the namespace again. The page draft is not touched: the row is read-only, so the clear is not one of the page's edits. A refusal toasts the server's sentence (the pin never refuses a clear, so lockedByEnv would misname a row lock or a 403).
  • @object-ui/i18n: four console.settingsView keys (savedBeneathPin, clearSaved, clearSavedConfirm, savedCleared) in all eleven packs (since feat(i18n)!: the Console's language packs follow the platform locale table, keyed by its tags, and Traditional Chinese (zh-Hant) ships #30902 merged: zh-Hans, zh-Hant and pt-BR replace zh and pt). Wording clears the saved value and never says delete.
  • .changeset/30665-clear-saved-beneath-env-pin.md: @object-ui/console and @object-ui/i18n patch.

What the server serves, measured on origin/main 41dc302496 before the first edit:

  • The namespace read (getNamespace → resolveMany(..., beneathPins = true)) answers a pinned key { source: 'env', locked: true, cascadeChain: [env entry (effective), ...stored cascade beneath] } via withStoredBeneath; the entries beneath lose effective and end with the default entry. A pinned key whose env value is null keeps its one env entry. A secret beneath the pin is storedSecretPresence on the service and masked entry by entry by redactSecretValues on the route, so the console sees the mask or null.
  • The clear (setMany, the [#30385] block): null / undefined for a key an in-force override pins passes the SettingsLockedError check and is judged by the row locks and write predicates like any clear; it writes at the key's registered scope (spec.scope ?? manifest.scope ?? 'tenant'), the same derivation the page passes as writeScope. So the control is gated on an entry at that scope: a value stored only at another rung could not be removed by this clear, and no control is drawn for it.

No new server field is needed; no file under packages/** changes.

Tests

ui/apps/console/src/pages/settings/__tests__/SettingsView.clearBeneathPin-30665.test.tsx (7 tests, whole page over a stubbed fetch and the real ../api):

  • the control appears on exactly the three pinned rows with a value stored at the write scope (text, masked secret, toggle) and not on a pinned row with nothing stored, a pinned row stored only above the write scope, or an unpinned row;
  • the line and the confirm say the environment value stays in force and never say delete;
  • confirm sends exactly {"allowed_ip_ranges":null}, toasts, and the row stays locked and read-only with no control left after the server's answer;
  • a masked secret's clear sends {"webhook_secret":null};
  • Cancel sends nothing;
  • an unsaved edit elsewhere stays in the draft and is not sent;
  • a refused clear toasts the server's sentence and keeps the control.

Verification

Local runs on head (see the report comment for the final sha)
  • pnpm --dir ui exec vitest run apps/console/src/pages/settings/ → Test Files 38 passed (38), Tests 304 passed (304).
  • pnpm --dir ui exec vitest run packages/i18n/ → Test Files 97 passed (97), Tests 1521 passed, 13 skipped (key parity across all ten packs included; on head fb666f8 after merging main: settings + i18n 136/136 files, 1949 passed, 13 skipped, eleven-pack parity included).
  • Ablation (node scripts/ablation-replace.mjs, source is what the console project resolves): the write-scope condition in storedBeneathPin widened to any stored rung → 2 failed / 5 passed (the offer test and the clear test, both seeing the extra control on the tenant row stored only at global); restored, blob equal to HEAD, git diff HEAD empty.
  • Derived gates (dispatch-gates.mjs --commands): every line run with stdin closed; exit codes in the report comment, reconciled with --ran.

Acceptance notes

  • The clear writes at once after its own confirm rather than through the page's Save bar. A pinned row is read-only, so a pending clear would have to draw a value the row never shows; the confirm is the row's own question, like an action_button's confirmText.
  • The i18n-locale-en chunk gains four strings; its per-chunk ceiling is unchanged. The eager-closure budget is weighed by CI's Build & E2E UI job (pnpm --dir ui run check:eager-closure), not re-run locally.
  • The first-run checklist renders SettingsField without onClearSaved, so it shows no control.

Generated by Claude Code

A settings field an OS_* variable pins now offers, when a value is stored
beneath the pin at the scope the page writes, a confirmed control that clears
that saved value; the environment value stays in force.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibzzesuj1TsQQD4hQjM
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 6, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#3 · os-tesla · session_01L9qibxW6qraU2Z3mwupUGp

Contract review

Served-tier: TIER_DEFAULT
Head-sha: 1583f7d9872b775d03453ee466ef6ec72a82a3f8
Local-runs: none

① Derived judgments

  • The declaration names the whole widening: four new leaves under console.settingsView (savedBeneathPin, clearSaved, clearSavedConfirm, savedCleared) in all ten published @object-ui/i18n locale packs, +4 lines each (en +8 with a comment), nothing removed or renamed (git diff --stat origin/main...1583f7d9: 14 files, +443/−3). No i18n-locale-en budget file moves; Build & E2E UI (eager closure) is green on this head.
  • No other published face moves: storedBeneathPin and the onClearSaved prop are console-app internals (ui/apps/console is not a library entry); no packages/** file in the diff, so the namespace read and the PUT door are the server's as merged (feat(service-settings): the namespace read lists what is stored beneath an env pin #30835 87fc795751, fix(service-settings): a clear goes through an env pin, so the allowed-IP lockout recovery survives removing the variable #30448).
  • Signal read, not invented: the server's cascadeChain entries carry scope global / tenant / user / default (settings-service.ts cascade walk) and, since feat(service-settings): the namespace read lists what is stored beneath an env pin #30835, the stored cascade beneath an in-force env entry. The control requires locked && source === 'env' and a non-null entry at the row's own write scope, so a value stored only at another scope (which this page's clear could not null) and the default entry never offer it; a secret's mask counts as stored, which is right (the clear target exists).
  • Write path: one PUT with { [key]: null } for that key alone, after the row's confirm; the page draft is not touched (pinned rows are read-only, so the clear is not a draft edit); a refusal shows the server's sentence instead of lockedByEnv. Seven cases in SettingsView.clearBeneathPin-30665.test.tsx pin show/hide by scope, the wording (no "delete"), the null write, the masked secret, Cancel as control, draft isolation and the refusal; the dev's ablation went red 2/7.

② Semver level

@object-ui/console patch and @object-ui/i18n patch, Clause-②: yes (widening), Metadata-protocol: none: additive keys and an additive UI control, no stored shape or protocol change. Correct.

③ Boundary flags

None. No server field was needed (the claim's stop did not trigger); no packages/**; wording follows #30385 ruling A ("clear the saved value", never "delete", env value stays in force).

④ Design judgment

Fits the card's Expected item by item: control only on a locked field with a value stored beneath the pin, none otherwise; the line says the environment value stays in force. Writing at once behind a confirm, rather than through the draft, matches a row that cannot be edited.

Implemented-by: claude/project-thread-1tgs4d
Reviewed-by: session_01L9qibxW6qraU2Z3mwupUGp

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#3 · os-tesla · session_01L9qibxW6qraU2Z3mwupUGp

维护者速读

席位核对 — ACCEPT · head fb666f8

  • 形态:15 个文件,+447/−3。check-governed-merges --pr 31039:NOT governed,450 行 ≤ 3000。Fixes #30665,目标 main。与认领 6693049942 及修订 6693075494(分支)一致,改动都在认领面内(ui/apps/console/src/pages/settings/ 与语言包)。本 PR 由本席派出的卡线程实现,席位不写码。
  • 契约:Clause-②: yes (widening)(4 个新键),Metadata-protocol: none,console + i18n patch。TIER_DEFAULT 复核 6693932801 在 1583f7d 上 PASS;之后的增量只有合并 main(feat(i18n)!: the Console's language packs follow the platform locale table, keyed by its tags, and Traditional Chinese (zh-Hant) ships #30902 把 zh/pt 改名为 zh-Hans/pt-BR,四个键随改名自动迁移)、zh-Hant 补四个繁体键、changeset 改「eleven packs」,席位逐行读过增量,不改契约判断(仍是新增键的 widening);PR 正文同步改为 11 个语言包。i18n-locale-en 预算未动。
  • 席位自己核对:读服务端级联链的 scope 取值(global / tenant / user / default)对照 storedBeneathPin:只认页面写入层级上的非空项,default 项和别的层级不会触发;密文掩码算作有保存值,正确。写入只发 { key: null } 一个键,草稿不动;被拒时显示服务端原句。
  • 前提实测:dev 报告 6693876763(status done)。合并 main 后 settings + i18n 136/136 文件(1,949 条通过,含 11 包键对齐),console type-check 0;消融 2/7 变红后恢复;派生门禁在 1583f7d 上 36/36 exit 0;合并后重新派生为 38 条(多 check:merge-driver、check:role-word),线程在本地重跑,结果尚未回报,以本 head 的 CI 为准。
  • 门禁 check-runs:本 head 每个检查最新一次 46 success、13 skipped,0 红;mergeable_state clean。
  • 落地条款:维护者常设令 「你应该是使用 pm-dispatch 技能自主审核和落地 pr」(2026-11-06T14:09Z)。中继 pr_ready + automerge_enable。Fixes:合并即关卡,摘 pm:dispatched。

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 6, 2026 20:37
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 6, 2026 20:38
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 6, 2026
Merged via the queue into main with commit 115a43a Nov 6, 2026
60 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-1tgs4d branch November 6, 2026 20:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Settings: an env-pinned field offers no way to clear the value saved beneath the pin

1 participant