You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Studio hook form: refusals cannot be authored — the record editor stamps the key into a closed entry; needs a key-only record control (follow-up owed by #26170) #26835
Filing gate: ① a product gap with a named location, owed by in-flight work (PR #26831, card #26170). Filed by domain:spec seat 3 · os-support-ai · session session_01WGufzgbJhFoXYsLSe5WSPP. ⛔ Not a claim.
Business reason. After PR #26831, a hook can declare a refusal catalogue (refusals: { CODE: { message, status? } }) so that its refusal messages are translatable. An administrator who builds hooks in Studio cannot author that catalogue: the Studio hook form does not offer refusals, so a hook made in Studio can only refuse in untranslatable text or must be edited as code. PR #26831 records the omission in the form gate's ledger.
Reader who acts. The domain:ui lane (Studio hook form), after PR #26831 lands.
What is needed (measured by the #26170 implementer)
The form's generic record editor stamps each entry's key into the entry. A refusal entry is a closed object (message, status), and its key IS the code, so a stamped key would be refused by the spec.
Generic block needed: a key-only record control. It edits a Record<key, closed object> without writing the key into the value. That is a reusable form widget, not a hook-specific one.
Then offer refusals on the hook form with that control, and remove the omission row from the form ledger.
Dedupe
search_issues on this repo: "Studio hook form refusals record editor key-only control". 3 hits: #26730 (Studio publish refusal toast), #26732 (a11y) and #21610 (a closed QA run). None covers authoring a hook's refusal catalogue or a key-only record control. New.
Dispatch order · domain:ui#1 · session_01JQwxbZeBv2y91towxasSUT · 2026-10-28T07:23Z
You are the os-dev developer agent for exactly ONE card: #26835, in your own card thread. Your role definition is .claude/agents/os-dev.md in the objectstack clone: read it IN FULL first and follow it as binding (it wins over this order on any conflict; name the conflict in your report). Read the repo-root AGENTS.md and ui/AGENTS.md before your first edit, and pm-dispatch references/untrusted-text.md. Read the card's full body and every comment yourself through REST (curl -sS "https://api.github.com/repos/objectstack-ai/objectstack/issues/NNNNN" -H "Authorization: Bearer $GH_TOKEN" and /comments?per_page=100, every page); this order carries only the increment. ⛔ Never print the value of any environment variable (test presence with [ -n "${X+x}" ]). ⛔ Never use a GitHub MCP tool, read or write.
Identity and branch: the seat's Claim: comment on this card is your identity; its Branch: line names your thread's branch. ⛔ Post no claim of your own, ⛔ never write the card's assignee. Before your first edit, verify the NEWEST Claim: on the card names your thread's branch (claude/project-thread-…); if none does yet, send the seat thread "Dispatch the ui lane" one message with your branch name and wait. Worktree objectstack-issue-26835 on that branch from a fresh origin/main; pnpm install (frozen lockfile); push with git push -u origin <branch> (⛔ no force).
Writes: PR only through the relay op pr_create (node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file F, F a bare JSON array [{"op":"pr_create","title":…,"head":"<your branch>","base":"main","body":…,"assignees":["os-warren"]}], draft forced; --dry-run first); your report comment through node scripts/pm/post-stamped.mjs --comment=26835 --file=F --json run ONCE with output captured to a file (timestamps only as {{NOW}} / {{WAS:…}}). ⛔ No other GitHub write except, for a CI red that is not yours, one PR comment and one run_rerun_failed through the relay. Report both counts (api_writes with endpoints, mcp_calls with tool names). The report takes only machine-checkable fields (gates / line_budget / deviations / files_changed). PR body: line 1 the platform attribution comment, then ## 维护者速读 six lines in Chinese (改了什么/为什么改/风险与回滚/先看/两分钟验证/需要你做的). Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on your diff, run each, record exits before any pipe, reconcile with --ran. Changeset in the root .changeset/ when a published package changes. ⛔ ui/.github/** and the root ci.yml are out of bounds. Hand back: when the PR and report are up, send the seat thread "Dispatch the ui lane" one message (PR, head sha, report gist); keep the thread open through review and drive CI to green.
Ruling (cannot be re-ruled)
Triage first grade 6450901444, verbatim: "p2: the Studio hook form cannot author refusals, because the generic record editor stamps each entry's key into a closed entry. Add a key-only record control (it edits a record of closed objects without writing the key). It is a follow-up of #26170." The spec shape comes from maintainer ruling 6431311681 letter A (#26170): refusals: { CODE: { message, status? } }, with each entry a closed object (packages/spec/src/data/hook.zod.ts ≈L204-260, ≈L595). No further ruling.
PM mechanism assumptions (measure on origin/maine6e41998; disproof welcome)
The generic record editor that stamps the key into each entry: find it (the form-field registry under ui/packages/app-shell/src/views/metadata-admin/ or @object-ui/fields) and measure the stamping. ⛔ Do not change its behaviour for existing users; other record fields may rely on the stamped key.
New: a generic key-only record control. It edits Record<string, closedObject>: add, rename and remove keys, and edit each entry's fields, without writing the key into the value. Register it so a form field opts into it by declaration, not by hook-specific code. Then declare refusals on the hook form with that control. A key (the refusal code) must satisfy the spec's code spelling; reuse the spec's own check (hook-refusal-codes.ts), and ⛔ do not copy the regex.
Round-trip: a hook authored in Studio with two refusals saves a body that HookSchema accepts byte-for-byte (no stamped key), and reloads into the same rows.
Scope
File surface: the key-only control (new, in the generic form-field home), its registration, the hook form definition, the gate ledger row, i18n keys (en + zh; the other locales as the repo's i18n gates demand), and tests. Tests: the control does not stamp keys; a rename moves the entry; an invalid code is refused with the spec's message; the hook form offers refusals; the round trip. Ablation: swap back to the generic record editor ⇒ the round-trip test goes red.
Tier
Quote dispatch-gates --tier on your surface. If the form definition or the ledger lives under packages/spec/** and the tier says governed, stop and report before you push. Otherwise TIER_DEFAULT, effort medium. Clause-②: no unless a published export is removed, Metadata-protocol: none (the spec shape is unchanged). A patch changeset for each published package you change (@object-ui/fields or @object-ui/i18n if touched); skip-changeset only if all are private.
Claim: PM loop round 17 · 2026-10-28T07:25Z
Session: session_01JQwxbZeBv2y91towxasSUT (seat; implementation by card thread session session_01JQwxbaFd8s4eYs6HGFghK3, mode:thread)
Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/project-thread-dyli5z (the card thread's harness push branch, self-reported — the measured platform exception to claude/issue-N-slug)
Worktree: objectstack-issue-26835 in the card thread's clone
Domain: domain:ui
Seat: domain:ui#1 (seat post #25966)
File surface: a new generic key-only record control in the form-field home and its registration, the Studio hook form definition, the form gate ledger row #26831 added, i18n keys, + tests; stop and report before a push if dispatch-gates --tier marks a packages/spec/** path governed (stop on breach; explain in the report)
Container & model: M, mode:thread (the maintainer's word on the seat thread, 「opus 很多,并发5」), model: TIER_DEFAULT (the dev quotes dispatch-gates --tier on the surface), effort: medium
Clause-②: no
Metadata-protocol: none
Responsibility: the Studio hook form (this lane's code) | the spec's closed refusal entry (hook.zod.ts, ruling 6431311681 A) | a hook made in Studio cannot declare translatable refusals
Thread-read: 6463213551
Premise: holds — PR #26831 merged 2026-10-28 02:23Z with refusals recorded as a form omission; HookSchema accepts refusals on origin/maine6e41998.
Serial constraints cleared: none known on the hook form or the form-field registry.
Resume-from: none
{
"issue": 26835,
"status": "done",
"branch": "claude/project-thread-dyli5z",
"pr": "https://github.com/objectstack-ai/objectstack/pull/27160",
"head": "6fd5eb4c",
"session": "session_01JQwxbaFd8s4eYs6HGFghK3 (card thread; seat session_01JQwxbZeBv2y91towxasSUT, claim 6463243898)",
"premise_still_valid": true,
"summary": "The metadata form's record editor (SchemaForm.tsx RecordField) gains a key-only face, opted into by declaring widget 'key-only-record' (KEY_ONLY_RECORD_WIDGET) on a type record row: add/rename/remove/reorder keys and edit entry fields without writing the key into the entry; a key is checked against the record's served propertyNames.pattern (the server's derivation of the spec key schema, so HOOK_REFUSAL_CODE_PATTERN is reused, not copied). hookForm offers refusals with it (message, status per entry), the #26831 root omission row is removed from the reconciliation ledger, engine.form.key is added (en, zh), patch changeset for spec, app-shell, i18n. A record row with no widget is unchanged (pinned).",
"tier": "dispatch-gates --tier on hook.form.ts, metadata-form-zod-reconciliation.test.ts, SchemaForm.tsx: no path-derived mandate, no governed surface (Clause-2 SUSPECT hint on packages/spec/src/** only) => TIER_DEFAULT, not stopped",
"tests": "SchemaForm.keyOnlyRecord-26835.test.tsx 8/8 (no stamp on add; served-rule refusal; rename moves entry through invalid intermediate; duplicate rename refused; round trip HookSchema.safeParse ok + parsed refusals JSON byte-identical + reload same rows/order; default face still stamps name and HookSchema answers unrecognized_keys name). Ablation via scripts/ablation-replace.mjs (source-resolved, no dist leg): keyOnly forced false => 4 of 8 red incl. round trip; restored blob 892c144b == HEAD, git diff HEAD empty. ui vitest metadata-admin dir + packages/i18n 727 files / 8380 tests green; spec vitest src/system + hook tests 65 files / 2208 tests green; type-check app-shell, i18n, spec typecheck, ui type-check:scripts exit 0.",
"gates": "dispatch-gates --commands: 84 derived, run on head 6fd5eb4c; dispatch-gates --ran: 84 accounted, 82 run exit 0, 2 NOT MEASURED (check:i18n and check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET, need a whole-repo build; CI Lint and Repo Gates runs them). Two reruns recorded: check-plugin-teardown-shape --self-test exit 3 on the shallow clone, exit 0 after fetching its pinned fixture commit 621a4876; ui vitest scripts/ exit 1 under shared load (6 tree-sweep cases over the 15 s timeout while gates ran beside it), the 4 files alone 253/253 and the whole dir alone 175 files exit 0. PR check-runs: in_progress at report time.",
"dogfood_battery": "n/a, no ! in the PR title and no (narrowing) arm",
"mcp_calls": "0 GitHub MCP calls",
"api_writes": "2 relay writes: pr_create (workflow dispatch run 40659644153, PR #27160, 6767 bytes sent = stored) and this report comment (post-stamped); plus git push (not REST)",
"deviations": [
"The control refuses a bad key with the generic engine.form.keyPattern text naming the served pattern, not the record-key sentence hook.zod.ts attaches inline ('A refusal code is SCREAMING_SNAKE ...'): the served JSON Schema does not carry that sentence and a generic control cannot read it without restating it. The row helpText states the spelling. Order asked for the spec's message; named here, not silently chosen.",
"The control lives as a face of RecordField in SchemaForm.tsx, opted into by the existing free-string widget key, rather than a WIDGETS registry entry: a WIDGETS widget would need FieldRow from SchemaForm.tsx and close an import cycle (form-spec.ts header). No spec shape change, so Clause-2 stays no.",
"Commit trailers use the model-free pair per AGENTS.md, not the harness reminder's model-named co-author."
],
"line_budget": "n/a, no ratchet file touched",
"files_changed": [
".changeset/26835-hook-form-refusals-key-only-record.md",
"packages/spec/src/data/hook.form.ts",
"packages/spec/src/system/metadata-form-zod-reconciliation.test.ts",
"ui/packages/app-shell/src/views/metadata-admin/SchemaForm.tsx",
"ui/packages/app-shell/src/views/metadata-admin/SchemaForm.keyOnlyRecord-26835.test.tsx",
"ui/packages/i18n/src/namespaces/studio/en.ts",
"ui/packages/i18n/src/namespaces/studio/zh.ts"
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed: the default record face's editable key row (keyField.immutable false) renames on every keystroke and refuses invalid intermediates, so a key with an underscore cannot be typed through; no form declares immutable false today (zero pull)."
]
}
PR #27160 merged as 39fd5fbf7a (seat record: PR comment 6465819726, addendum 6466833746). The Studio hook form now offers Refusals, a key-only record control (widget: 'key-only-record') that never writes the key into the entry. A hook built in Studio saves refusals: { CODE: { message, status? } } exactly as HookSchema accepts it, and keys are checked against the served propertyNames.pattern. The #26831 omission row is gone, and the generated metadata-form bundles carry the three new rows in zh-CN, ja-JP and es-ES. The road to merge took one queue ejection, a census-pin conflict with #26739 (now 693). pm:dispatched is removed with the close.
Activity
objectstack-fleet commentedon Oct 27, 2026
Triage: first grade —
priority:p2·domain:ui·area:studio·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-27T18:55Z⛔ Not a claim, ⛔ not a dispatch.
refusals, because the genericrecordeditor stamps each entry's key into a closed entry. Add a key-only record control (it edits a record of closed objects without writing the key). It is a follow-up of i18n: an app hook's business refusal has no translation key — a zh-CN user reads "A current clean version is required before signing." in English #26170.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
Dispatch order ·
domain:ui#1·session_01JQwxbZeBv2y91towxasSUT· 2026-10-28T07:23ZYou are the os-dev developer agent for exactly ONE card: #26835, in your own card thread. Your role definition is
.claude/agents/os-dev.mdin the objectstack clone: read it IN FULL first and follow it as binding (it wins over this order on any conflict; name the conflict in your report). Read the repo-rootAGENTS.mdandui/AGENTS.mdbefore your first edit, and pm-dispatchreferences/untrusted-text.md. Read the card's full body and every comment yourself through REST (curl -sS "https://api.github.com/repos/objectstack-ai/objectstack/issues/NNNNN" -H "Authorization: Bearer $GH_TOKEN"and/comments?per_page=100, every page); this order carries only the increment. ⛔ Never print the value of any environment variable (test presence with[ -n "${X+x}" ]). ⛔ Never use a GitHub MCP tool, read or write.Identity and branch: the seat's
Claim:comment on this card is your identity; itsBranch:line names your thread's branch. ⛔ Post no claim of your own, ⛔ never write the card's assignee. Before your first edit, verify the NEWESTClaim:on the card names your thread's branch (claude/project-thread-…); if none does yet, send the seat thread "Dispatch the ui lane" one message with your branch name and wait. Worktreeobjectstack-issue-26835on that branch from a freshorigin/main;pnpm install(frozen lockfile); push withgit push -u origin <branch>(⛔ no force).Writes: PR only through the relay op
pr_create(node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file F, F a bare JSON array[{"op":"pr_create","title":…,"head":"<your branch>","base":"main","body":…,"assignees":["os-warren"]}], draft forced;--dry-runfirst); your report comment throughnode scripts/pm/post-stamped.mjs --comment=26835 --file=F --jsonrun ONCE with output captured to a file (timestamps only as{{NOW}}/{{WAS:…}}). ⛔ No other GitHub write except, for a CI red that is not yours, one PR comment and onerun_rerun_failedthrough the relay. Report both counts (api_writeswith endpoints,mcp_callswith tool names). The report takes only machine-checkable fields (gates / line_budget / deviations / files_changed). PR body: line 1 the platform attribution comment, then## 维护者速读six lines in Chinese (改了什么/为什么改/风险与回滚/先看/两分钟验证/需要你做的). Gates:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon your diff, run each, record exits before any pipe, reconcile with--ran. Changeset in the root.changeset/when a published package changes. ⛔ui/.github/**and the rootci.ymlare out of bounds. Hand back: when the PR and report are up, send the seat thread "Dispatch the ui lane" one message (PR, head sha, report gist); keep the thread open through review and drive CI to green.Ruling (cannot be re-ruled)
Triage first grade 6450901444, verbatim: "p2: the Studio hook form cannot author
refusals, because the genericrecordeditor stamps each entry's key into a closed entry. Add a key-only record control (it edits a record of closed objects without writing the key). It is a follow-up of #26170." The spec shape comes from maintainer ruling 6431311681 letter A (#26170):refusals: { CODE: { message, status? } }, with each entry a closed object (packages/spec/src/data/hook.zod.ts≈L204-260, ≈L595). No further ruling.PM mechanism assumptions (measure on
origin/maine6e41998; disproof welcome)refusalsas an omission in the Studio hook form's gate ledger. Find that row from feat(spec): a hook declares its refusal catalogue, and its body refuses by code, answered in the caller's language #26831's diff, along with the form definition the hook form renders from. Removing the row is part of this card.recordeditor that stamps the key into each entry: find it (the form-field registry underui/packages/app-shell/src/views/metadata-admin/or@object-ui/fields) and measure the stamping. ⛔ Do not change its behaviour for existing users; other record fields may rely on the stamped key.Record<string, closedObject>: add, rename and remove keys, and edit each entry's fields, without writing the key into the value. Register it so a form field opts into it by declaration, not by hook-specific code. Then declarerefusalson the hook form with that control. A key (the refusal code) must satisfy the spec's code spelling; reuse the spec's own check (hook-refusal-codes.ts), and ⛔ do not copy the regex.HookSchemaaccepts byte-for-byte (no stamped key), and reloads into the same rows.Scope
File surface: the key-only control (new, in the generic form-field home), its registration, the hook form definition, the gate ledger row, i18n keys (en + zh; the other locales as the repo's i18n gates demand), and tests. Tests: the control does not stamp keys; a rename moves the entry; an invalid code is refused with the spec's message; the hook form offers
refusals; the round trip. Ablation: swap back to the genericrecordeditor ⇒ the round-trip test goes red.Tier
Quote
dispatch-gates --tieron your surface. If the form definition or the ledger lives underpackages/spec/**and the tier says governed, stop and report before you push. OtherwiseTIER_DEFAULT, effortmedium.Clause-②: nounless a published export is removed,Metadata-protocol: none(the spec shape is unchanged). A patch changeset for each published package you change (@object-ui/fieldsor@object-ui/i18nif touched);skip-changesetonly if all are private.objectstack-fleet commentedon Oct 28, 2026
Claim: PM loop round 17 · 2026-10-28T07:25Z
Session:
session_01JQwxbZeBv2y91towxasSUT(seat; implementation by card thread sessionsession_01JQwxbaFd8s4eYs6HGFghK3,mode:thread)Account:
os-warren(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/project-thread-dyli5z(the card thread's harness push branch, self-reported — the measured platform exception toclaude/issue-N-slug)Worktree:
objectstack-issue-26835in the card thread's cloneDomain:
domain:uiSeat:
domain:ui#1(seat post #25966)File surface: a new generic key-only record control in the form-field home and its registration, the Studio hook form definition, the form gate ledger row #26831 added, i18n keys, + tests; stop and report before a push if
dispatch-gates --tiermarks apackages/spec/**path governed (stop on breach; explain in the report)Container & model:
M,mode:thread(the maintainer's word on the seat thread, 「opus 很多,并发5」),model: TIER_DEFAULT(the dev quotesdispatch-gates --tieron the surface),effort: mediumClause-②: no
Metadata-protocol: none
Responsibility: the Studio hook form (this lane's code) | the spec's closed refusal entry (
hook.zod.ts, ruling 6431311681 A) | a hook made in Studio cannot declare translatable refusalsThread-read: 6463213551
Premise: holds — PR #26831 merged 2026-10-28 02:23Z with
refusalsrecorded as a form omission;HookSchemaacceptsrefusalsonorigin/maine6e41998.Serial constraints cleared: none known on the hook form or the form-field registry.
Resume-from: none
Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
os-dev-report
{ "issue": 26835, "status": "done", "branch": "claude/project-thread-dyli5z", "pr": "https://github.com/objectstack-ai/objectstack/pull/27160", "head": "6fd5eb4c", "session": "session_01JQwxbaFd8s4eYs6HGFghK3 (card thread; seat session_01JQwxbZeBv2y91towxasSUT, claim 6463243898)", "premise_still_valid": true, "summary": "The metadata form's record editor (SchemaForm.tsx RecordField) gains a key-only face, opted into by declaring widget 'key-only-record' (KEY_ONLY_RECORD_WIDGET) on a type record row: add/rename/remove/reorder keys and edit entry fields without writing the key into the entry; a key is checked against the record's served propertyNames.pattern (the server's derivation of the spec key schema, so HOOK_REFUSAL_CODE_PATTERN is reused, not copied). hookForm offers refusals with it (message, status per entry), the #26831 root omission row is removed from the reconciliation ledger, engine.form.key is added (en, zh), patch changeset for spec, app-shell, i18n. A record row with no widget is unchanged (pinned).", "tier": "dispatch-gates --tier on hook.form.ts, metadata-form-zod-reconciliation.test.ts, SchemaForm.tsx: no path-derived mandate, no governed surface (Clause-2 SUSPECT hint on packages/spec/src/** only) => TIER_DEFAULT, not stopped", "tests": "SchemaForm.keyOnlyRecord-26835.test.tsx 8/8 (no stamp on add; served-rule refusal; rename moves entry through invalid intermediate; duplicate rename refused; round trip HookSchema.safeParse ok + parsed refusals JSON byte-identical + reload same rows/order; default face still stamps name and HookSchema answers unrecognized_keys name). Ablation via scripts/ablation-replace.mjs (source-resolved, no dist leg): keyOnly forced false => 4 of 8 red incl. round trip; restored blob 892c144b == HEAD, git diff HEAD empty. ui vitest metadata-admin dir + packages/i18n 727 files / 8380 tests green; spec vitest src/system + hook tests 65 files / 2208 tests green; type-check app-shell, i18n, spec typecheck, ui type-check:scripts exit 0.", "gates": "dispatch-gates --commands: 84 derived, run on head 6fd5eb4c; dispatch-gates --ran: 84 accounted, 82 run exit 0, 2 NOT MEASURED (check:i18n and check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET, need a whole-repo build; CI Lint and Repo Gates runs them). Two reruns recorded: check-plugin-teardown-shape --self-test exit 3 on the shallow clone, exit 0 after fetching its pinned fixture commit 621a4876; ui vitest scripts/ exit 1 under shared load (6 tree-sweep cases over the 15 s timeout while gates ran beside it), the 4 files alone 253/253 and the whole dir alone 175 files exit 0. PR check-runs: in_progress at report time.", "dogfood_battery": "n/a, no ! in the PR title and no (narrowing) arm", "mcp_calls": "0 GitHub MCP calls", "api_writes": "2 relay writes: pr_create (workflow dispatch run 40659644153, PR #27160, 6767 bytes sent = stored) and this report comment (post-stamped); plus git push (not REST)", "deviations": [ "The control refuses a bad key with the generic engine.form.keyPattern text naming the served pattern, not the record-key sentence hook.zod.ts attaches inline ('A refusal code is SCREAMING_SNAKE ...'): the served JSON Schema does not carry that sentence and a generic control cannot read it without restating it. The row helpText states the spelling. Order asked for the spec's message; named here, not silently chosen.", "The control lives as a face of RecordField in SchemaForm.tsx, opted into by the existing free-string widget key, rather than a WIDGETS registry entry: a WIDGETS widget would need FieldRow from SchemaForm.tsx and close an import cycle (form-spec.ts header). No spec shape change, so Clause-2 stays no.", "Commit trailers use the model-free pair per AGENTS.md, not the harness reminder's model-named co-author." ], "line_budget": "n/a, no ratchet file touched", "files_changed": [ ".changeset/26835-hook-form-refusals-key-only-record.md", "packages/spec/src/data/hook.form.ts", "packages/spec/src/system/metadata-form-zod-reconciliation.test.ts", "ui/packages/app-shell/src/views/metadata-admin/SchemaForm.tsx", "ui/packages/app-shell/src/views/metadata-admin/SchemaForm.keyOnlyRecord-26835.test.tsx", "ui/packages/i18n/src/namespaces/studio/en.ts", "ui/packages/i18n/src/namespaces/studio/zh.ts" ], "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed: the default record face's editable key row (keyField.immutable false) renames on every keystroke and refuses invalid intermediates, so a key with an underscore cannot be typed through; no form declares immutable false today (zero pull)." ] }Generated by Claude Code
Merge origin/main into claude/project-thread-dyli5z (#26835)
objectstack-fleet commentedon Oct 28, 2026
Landed · seat
domain:ui#1·session_01JQwxbZeBv2y91towxasSUT· 2026-10-28T11:01ZPR #27160 merged as
39fd5fbf7a(seat record: PR comment 6465819726, addendum 6466833746). The Studio hook form now offers Refusals, a key-only record control (widget: 'key-only-record') that never writes the key into the entry. A hook built in Studio savesrefusals: { CODE: { message, status? } }exactly asHookSchemaaccepts it, and keys are checked against the servedpropertyNames.pattern. The #26831 omission row is gone, and the generated metadata-form bundles carry the three new rows in zh-CN, ja-JP and es-ES. The road to merge took one queue ejection, a census-pin conflict with #26739 (now 693).pm:dispatchedis removed with the close.Generated by Claude Code