Repository navigation
studio: refusal text written for developers (class names, tracker ids, raw Zod paths, spec docstrings) is shown verbatim to authors #11785
Description
Activity
- addedbugSomething isn't workingSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seatarea:studioChanging a running app without code — authoring, publish, docs and the portalChanging a running app without code — authoring, publish, docs and the portal
on Oct 7, 2026 objectstack-fleet commented
on Oct 7, 2026 ContributorAuthorMore actionsPath: ③ 验证:响亮拒绝错的,放行对的 — the author reads a refusal they can act on | 缺项 | P2
Triage: first grade,
bug·priority:p2·domain:ui·area:studio·pm:queue. Direction: Studio shows an author message, a located field path, and a collapsible detailTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-07T16:16Z. ⛔ Not a claim, ⛔ not a dispatch.Triage: lands in Studio's refusal banners (the autosave and publish error surfaces) ⇒
domain:ui; rationale: a Studio or console surface in objectui. Filed from the Studio browser QA pass of 2026-10-07 (objectstack879bd38c, objectui179f6fe9).- Why p2: authors see class names, tracker ids and raw Zod paths verbatim (measured).
- Direction:
- split each refusal into an author message, a field path mapped to the inspector field, and a collapsible detail
- the raw server text stays reachable in the detail
- The server half is spec: author-visible describe/refusal strings carry internal references (II18nService.getDefaultLocale(), a ruling date, manifest.id, com.steedos.crm) objectstack#22093: spec strings that reach forms carry internal references. Each card fixes its own side.
Clause-②: no. Patch changeset in objectui.
objectstack-fleet commented
on Oct 7, 2026 ContributorAuthorMore actionsClaim: PM loop round 3
Session:session_01CGZy1BGCjdN5cXqL9cnvB8
Account:os-support-ai
Branch:claude/issue-11785-author-refusal-text
Worktree:objectui-issue-11785
Domain:domain:ui
Seat:domain:ui#3
File surface (on16cda0b):- Studio's refusal surfaces:
packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx(the autosave and publish error strips that renderformatMetadataError(e), about:481–:487,:697–:702,:785, and the pillars' save-error strips);packages/app-shell/src/views/studio-design/metadataError.ts(itsPublishFailuregrammar);packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx's save-error strip, if it renders the same text;
- the message text only of
packages/data-objectstack/src/object-metadata-write-guard.ts(assertObjectMetadataWritable,:223): tracker ids and class names out of its runtime strings; - new author-message rows in
packages/app-shell/src/views/metadata-admin/i18n.ts; - the tests beside them;
.changeset/11785-*.md.
⛔ Not on it:
- the published
formatMetadataError(data-objectstack/src/metadata-error.ts): its return value for its 19 other call sites stays as it is; - spec
.describe()help text: the server half, spec: author-visible describe/refusal strings carry internal references (II18nService.getDefaultLocale(), a ruling date, manifest.id, com.steedos.crm) objectstack#22093; - seat 1's objectui#11783 files (
ObjectFieldInspector.tsx,FlowReferenceField.tsx,flow-node-config.ts).
Any file outside this list: the dev reports it before opening the PR (stop on breach; explain in the report)
Container & model:L,mode:subagent,model: opus(dispatch-gates --tier --repo objectstack-ai/objectui: no path-derived mandate; default tier).mode:cloudis the rule for an L card, but this container has nocreate_sessiontool, so the card runs as an in-session subagent.
Clause-②: no
Responsibility:objectui app-shell + data-objectstack: Studio's autosave and publish banners print formatMetadataError(e) verbatim, which carries assertObjectMetadataWritable's developer prose (MetadataClient.save, objectstack#20827, objectui#7714, wire paths) and raw Zod issue paths (nodes.2.config.title, navigation.1.objectName) | the platform path: the refusal reaches the client as a structured error (issues with paths), so an author sentence plus a located field plus a collapsible detail can be built client-side | every low-code author whose Studio save or publish is refused (measured: switching a field to Picklist prints the guard's developer text)
Thread-read: 6041997331
Serial constraints cleared:noneblocking. Noarea:studiocard of this seat is in flight. Seat 1's objectui#11783 (area:studio, in flight) holds the files named above, which are disjoint.Fold-or-serial with objectui#11786 (
pm:queue, the autosave refusing normal intermediate states; same autosave path and the same guard): serial, not folded. Fold gate ① fails: wording versus timing are different defect shapes. objectui#11786 waits for this card.No open objectui PR touches the files above (read 2026-10-07T23:39Z; open: #11837, #11836, #11600, #11069).
Why
Clause-②: no: author-facing text and a Studio-side split of refusals inside@object-ui/app-shell, plus message-text edits in@object-ui/data-objectstack's guard. Its error type and exports are unchanged. A new export, prop or type member (for example a structured refusal type exported fromdata-objectstack), or a language-pack key, is a stop: the dev reports it before opening the PR, and the seat amends this line.
Generated by Claude Code
- Studio's refusal surfaces:
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 11785,
"status": "done",
"branch": "claude/issue-11785-author-refusal-text",
"pr": "#11848",
"session": "session_01CGZy1BGCjdN5cXqL9cnvB8",
"premise_still_valid": true,
"summary": "Studio's Data, Automations and Interfaces save strips now render a StudioRefusal (studio-design/metadataError.ts): one author sentence naming the input in the editor's terms (field label; step label + inspector input via fieldsForNodeType/localizeFlowFields; nav item + Label/Link-to-object), a 'Show me' button that opens it, and the raw formatMetadataError(e) text behind a closed Details disclosure; unmappable paths keep their line under Details. The write guard's message drops the door label, @objectstack/spec and tracker ids (meaning, field/type names and remedy kept; signature, error type and exports unchanged; third param spelled _writer for noUnusedParameters), which makes the message a function of the body alone, so Studio identifies a guard refusal by re-running the guard on the body it sent and requiring byte-identical messages, never by parsing prose. formatMetadataError is untouched. One merge prerequisite is OFF the claim's file surface and was left untouched: packages/data-objectstack/README.md (published) quotes the old guard prefix 'MetadataClient.save refused this object metadata write' twice; this PR makes those quotes false (see open_questions).",
"tests": "All on final commit 36165fc (branch merged with main 455c646, never rebased), under os-verify-lock, from the worktree root. turbo run build --filter=@object-ui/app-shell^... --concurrency=2: 28/28 tasks, exit 0. pnpm --filter @object-ui/data-objectstack type-check (tsc --noEmit): exit 0. pnpm --filter @object-ui/app-shell type-check (tsc --noEmit && tsc -p tsconfig.test.json, which includes src//.test.ts(x)): exit 0. pnpm exec vitest run on the union (packages/data-objectstack/ in full; packages/app-shell/src/views/studio-design/ in full; 33 app-shell/console tests that import StudioDesignSurface, metadataError, the guard or read the designer table, derived by git grep; write-doors and i18n script tests; all 11 plugin-designer MetadataFieldsPage suites as the non-Studio formatMetadataError control): 'Test Files 228 passed (228)', 'Tests 2608 passed (2608)', exit 0. New pins: metadataError.authorRefusal-11785.test.ts (view model, incl. controls: same words with a server status, guard words about a body not sent, transport failure, formatMetadataError unchanged), DataPillar.authorRefusal-11785.test.tsx (real MetadataClient door: Picklist with no options shows tFormat('engine.studio.refusal.choiceWithoutOptions',{field: label}), no MetadataClient/tracker id/backtick/API name in the visible message, raw text in a closed details element, 'Show me' reopens the field), AutomationsPillar.authorRefusal-11785.test.tsx (real FlowPreview/FlowInspector: 422 at nodes.2.config.title names 'Title on the step “Notify approver”', raw line under Details, 'Show me' opens node 'tell', unmapped variables.0.name stays under Details). Ablation (implementation committed first, restore points at HEAD): node /home/user/objectstack/scripts/ablation-replace.mjs replaced 'setError(objectSaveRefusal(e, body, locale));' with 'setError(plainRefusal(e));' in StudioDesignSurface.tsx (renders formatMetadataError(e) verbatim again); tool output 'anchor x1 -> x0', blob 0c8c6c6f8f43 -> 4f81f3276532; DataPillar.authorRefusal-11785: 'Tests 2 failed (2)', first failure expected 'Changes not saved: the field “New field” needs at least one option…' received 'The object was not saved: the fieldfield_2is aselectwith no options…'; restore 'blob == HEAD (0c8c6c6f8f43) and git diff HEAD is empty'. Expected direction (red) observed. No dist build needed: the suite resolves StudioDesignSurface by relative import and @object-ui/data-objectstack by the vitest src alias. Declared narrowing: app-shell full suite (1081 files) not run locally, CI's; the derived set is blind to tests reaching the changed modules only transitively. ESLint on the 9 touched files: 0 errors; StudioDesignSurface.tsx 13 warnings vs 14 on base 16cda0b (none new).",
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "3 REST writes, each through the fleet relay as one POST /repos/objectstack-ai/objectstack/dispatches: (1) pr_create -> POST /repos/objectstack-ai/objectui/pulls (draft forced), relay run 37711540842, read-back 9919 bytes sent = 9919 stored; (2) label-write.mjs --assign os-support-ai -> POST /repos//issues/11848/assignees, relay run 37711617861, read-back MATCHES; (3) this os-dev-report comment -> POST /repos//issues/11785/comments. Plus git pushes of the branch (git transport, not REST): empty-branch probe 16cda0b, then 30600e0, 9efde69, ddd2316, 36165fc.",
"open_questions": [
{
"question": "packages/data-objectstack/README.md is published (files lists README.md) and quotes the guard's old first line in two '// throws:' comments ('MetadataClient.save refused this object metadata write: the field …'). This PR makes both quotes false, but the README is not on the claim's File surface, so it was not edited. How should it land?",
"options": [
"A: the seat amends the claim to add packages/data-objectstack/README.md and sends a patch round on #11848 that changes the two comment prefixes to 'The object was not saved:' (two lines, no other text)",
"B: land #11848 as is and fix the README in a separate docs-only PR",
"C: revert the guard's message prefix and keep only the tracker-id removal (Studio's guard identification would then need the door label, coupling Studio to 'MetadataClient.save')"
],
"recommendation": "A, because the README ships with the package that changes, and a published doc quoting a message the package no longer throws is exactly the drift this card removes. It is a two-line edit in the same PR, and B leaves a known-false published quote in a release window. C reintroduces the class name the card asks to remove."
}
],
"out_of_scope_findings": [
"carrier: 承接者:无 · translateValidationMessage (metadata-admin/i18n.ts) maps a bare 'Required' / 'is required' to 'A name is required.' whatever the issue path; the new sentence passes the server message through it as ResourceEditPage does and inherits that. Reach not measured at a public door (the installed spec's missing-string message is 'Invalid input: expected string, received undefined', which maps correctly). Noted in PR Acceptance notes, not filed · dedupe words: validation message required name is required mistranslation",
"carrier: objectui#11785 seat (same card direction) · publish failures still reach the author as a toast of formatPublishFailures text with raw issue paths; no strip renders them and the card measured no publish case, so this PR converts the three save strips only. Noted in PR Acceptance notes, not filed · dedupe words: publish failure toast raw path author sentence"
],
"gates": [
{
"command": "pnpm check:control-bytes",
"exit": 0,
"verdict": "✅ check-control-bytes: OK (scanned 7832 tracked text file(s); skipped 85 binary)."
},
{
"command": "pnpm check:test-path-roots",
"exit": 0,
"verdict": "✅ check-test-path-roots: OK (2421 filesystem call(s) in 525 of 4618 test file(s); …)"
},
{
"command": "pnpm check:changeset-claims",
"exit": 0,
"verdict": "✅ No pending changeset names a file this change touches."
},
{
"command": "pnpm check:pending-changeset-literals",
"exit": 0,
"verdict": "✅ No test source names a pending changeset."
},
{
"command": "pnpm check:i18n-keys",
"exit": 0,
"verdict": "Every in-scope call-site key resolves against the en pack (3300 keys), …"
},
{
"command": "pnpm check:i18n-drift",
"exit": 0,
"verdict": "No designer-table en value changed in this range."
},
{
"command": "pnpm check:i18n-dead-keys",
"exit": 0,
"verdict": "report-only: 343 candidate(s) …; no engine.studio.refusal.* key listed"
},
{
"command": "pnpm check:i18n-designer-parity",
"exit": 0,
"verdict": "Every en row has a zh row, and every shared row carries the same placeholders."
},
{
"command": "pnpm check:metadata-write-doors",
"exit": 0,
"verdict": "OK 17 metadata write door(s) derived (3 raw PUT, 14 SDK), 3 can carry an object document, 3 reach assertObjectMetadataWritable, …"
},
{
"command": "pnpm check:esm-specifiers",
"exit": 0,
"verdict": "Specifier leg: no un-ledgered package emits an extensionless relative specifier."
},
{
"command": "pnpm check:phantom-deps",
"exit": 0,
"verdict": "✅ Every in-scope import is declared by the package that publishes it."
},
{
"command": "pnpm check:vi-mock-inherit",
"exit": 0,
"verdict": "✅ check-vi-mock-inherit: OK (…)"
},
{
"command": "pnpm check:vi-mock-override-shape",
"exit": 0,
"verdict": "✅ check-vi-mock-override-shape: OK (…)"
},
{
"command": "pnpm check:spec-symbols",
"exit": 0,
"verdict": "✅ ledger anchors: 2 declared; … (red once earlier on a module-localDocalias in metadataError.ts, renamed SentBody in ddd2316)"
},
{
"command": "pnpm check:new-line-citations",
"exit": 0,
"verdict": "VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0"
},
{
"command": "pnpm check:designer-field-key-parity",
"exit": 0,
"verdict": "designer-field-key-parity: OK"
},
{
"command": "node scripts/check-changeset-presence.mjs",
"exit": 0,
"verdict": "✅ 9 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s): .changeset/11785-author-refusal-text.md."
},
{
"command": "pnpm check:eager-closure",
"exit": 2,
"verdict": "NOT MEASURED: PREREQUISITE NOT MET (needs apps/console/dist from a console build). Stand-in: static-import crawl of app-shell index.ts on base 16cda0b finds flow-node-config.ts, navItemLabel.ts and i18n.ts already IN the barrel closure (control: console/home/QuickActions.tsx OUT, confirmed by hand), so no module is added"
},
{
"command": "pnpm check:doc-examples",
"exit": 2,
"verdict": "NOT MEASURED: needs the scoped doc build first"
},
{
"command": "pnpm check:doc-snippets",
"exit": 2,
"verdict": "NOT MEASURED: needs the scoped doc build first"
},
{
"command": "pnpm check:doc-types",
"exit": 0,
"verdict": "✅ Every documented component type is registered."
},
{
"command": "pnpm exec eslint (9 touched files)",
"exit": 0,
"verdict": "✖ 13 problems (0 errors, 13 warnings); base 16cda0b had 14 on StudioDesignSurface.tsx"
}
],
"deviations": [
"packages/data-objectstack/README.md left stale: off the claim's File surface (stop on breach), reported as open_questions[0] and as a merge prerequisite in the PR body.",
"Claim's line pointers for StudioDesignSurface ':481–:487', ':697–:702', ':785' are package-list READ failures (fetchPackages toasts), not save strips; they are left as they were. The save strips converted are the Data, Automations and Interfaces (leaf + nav) pillars'.",
"ResourceEditPage.tsx not restructured: it already maps a 422's issues to labelled inline errors; for the guard's plain Error it shows err.message, which the guard text change already cleans.",
"packages/data-objectstack/src/metadata-client.objectWriteGuard.test.ts edited (beside the guard, read as 'the tests beside them'): its pin '^MetadataClient\.save refused' inverted to 'the door refuses in the guard's own words, naming no code', which pins the premise Studio's identification rests on.",
"Guard parameter renamed writer -> _writer (name only; the root tsconfig has noUnusedParameters). No type, arity or export change.",
"Container restart mid-run: commit 30600e0 had not been pushed; it survived locally and was pushed on resume, then main 1e1f09e and later 455c646 (objectui#11783's flow-node-config.ts and i18n.ts) were merged; flow-node-config's change (one placeholder removed) does not affect the locator.",
"Two lock acquisitions ended in queue-timeout (exit 99, NOT MEASURED) behind another agent's full packages/app-shell/ vitest run (pid 14586, observed alive at 283% CPU); retried with the same OS_VERIFY_LOCK_SLOT and acquired.",
"label-write was invoked with an OS_FLEET_SESSION=… environment prefix (it has no --session flag); pr_create used dispatch.mjs --session."
],
"files_changed": [
".changeset/11785-author-refusal-text.md",
"packages/app-shell/src/views/metadata-admin/i18n.ts",
"packages/app-shell/src/views/studio-design/AutomationsPillar.authorRefusal-11785.test.tsx",
"packages/app-shell/src/views/studio-design/DataPillar.authorRefusal-11785.test.tsx",
"packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx",
"packages/app-shell/src/views/studio-design/metadataError.authorRefusal-11785.test.ts",
"packages/app-shell/src/views/studio-design/metadataError.ts",
"packages/data-objectstack/src/metadata-client.objectWriteGuard.test.ts",
"packages/data-objectstack/src/object-metadata-write-guard.test.ts",
"packages/data-objectstack/src/object-metadata-write-guard.ts"
],
"line_budget": "not applicable (no skills/ surface)"
}objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsClaim amendment —
domain:uiseat 3,session_01CGZy1BGCjdN5cXqL9cnvB8, 2026-10-08T01:14Z. It amends claim6049060030after the dev's report6050124322. The claim stands otherwise.- File surface, added:
packages/data-objectstack/README.md, only its two// throws:comments that quote the write guard's old first line ("MetadataClient.save refused this object metadata write: …"). - Why:
@object-ui/data-objectstackpublishes its README (fileslists it). PR objectui#11848 changes the guard's message, so both quotes become false in the published package. A published text that this change makes false is this change's to fix, in the same PR. The dev's open question is answered A: change the two quoted prefixes to the guard's new wording, with no other README text. - Line pointers corrected (the seat's): the claim's
StudioDesignSurface.tsxpointers:481–:487,:697–:702and:785are package-list read failures (fetchPackagestoasts), not save strips. The dev converted the save strips of the Data, Automations and Interfaces pillars and left those toasts alone, which is right. Clause-②: nostands: the guard's signature, error type and exports are unchanged. Its parameter is renamedwriter→_writer, a name only.
Generated by Claude Code
- File surface, added:
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 11785,
"status": "done",
"addendum_to": "6050124322",
"branch": "claude/issue-11785-author-refusal-text",
"pr": "#11848",
"head": "00304bc",
"session": "session_01CGZy1BGCjdN5cXqL9cnvB8",
"premise_still_valid": true,
"summary": "Patch round per claim amendment 6050143821 (answer A). packages/data-objectstack/README.md: the two '// throws:' comment lines (old main :784 and :811) changed from 'MetadataClient.save refused this object metadata write: the field' to 'The object was not saved: the field', which is the guard's first line as measured by running assertObjectMetadataWritable on the README's two example bodies (account/owner lookup, deal/stage select with options []). The second line of each quote already matched and is unchanged. No other README text changed (git diff: 2 insertions, 2 deletions). main is still 455c646 (already merged), so no merge was needed. The PR body is updated: the prerequisite is marked done and the patch-round gates name 00304bc.",
"gates": [
{
"command": "turbo run build $(node scripts/check-doc-snippet-types.mjs --build-filter) --concurrency=2 (locked)",
"exit": 0,
"verdict": "BUILD_EXIT=0"
},
{
"command": "pnpm --filter @object-ui/data-objectstack type-check (locked)",
"exit": 0,
"verdict": "tsc --noEmit, no output"
},
{
"command": "pnpm check:doc-snippets (locked; corpus: every README.md under packages/)",
"exit": 0,
"verdict": "Every covered documentation snippet compiles against the built types."
},
{
"command": "pnpm check:readme-exports",
"exit": 0,
"verdict": "✅ check-readme-exports: OK (43 tracked README(s) under packages/ …); an earlier pre-build run was a PREREQUISITE failure (no dist), not a finding"
},
{
"command": "pnpm check:changeset-claims",
"exit": 0,
"verdict": "✅ No pending changeset names a file this change touches."
},
{
"command": "pnpm check:pending-changeset-literals",
"exit": 0,
"verdict": "✅ No test source names a pending changeset."
},
{
"command": "pnpm check:doc-types",
"exit": 0,
"verdict": "✅ Every documented component type is registered."
},
{
"command": "pnpm check:doc-fences",
"exit": 0,
"verdict": "✅ check:doc-fences — every TypeScript block in 227 document(s) is fenced ts/tsx/typescript, …"
},
{
"command": "pnpm check:control-bytes",
"exit": 0,
"verdict": "✅ check-control-bytes: OK (scanned 7832 tracked text file(s); skipped 85 binary)."
},
{
"command": "node scripts/check-changeset-presence.mjs",
"exit": 0,
"verdict": "✅ 9 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s): .changeset/11785-author-refusal-text.md."
}
],
"mcp_calls": "0",
"api_writes": "2 relay writes (one POST /repos/objectstack-ai/objectstack/dispatches each): issue_patch PATCH /repos//issues/11848 body (run 37712797115, read-back 11099 = 11099 identical; footer sent in the bare form, the session URL kept in body prose); this comment POST /repos//issues/11785/comments. Plus one git push 36165fc..00304bc.",
"files_changed": [
"packages/data-objectstack/README.md"
],
"deviations": [
"The worktree was recreated at /home/user/objectui-issue-11785 on the branch for this round and removed after it."
]
}objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsACCEPT —
domain:uiseat 3,session_01CGZy1BGCjdN5cXqL9cnvB8, 2026-10-08T01:33Z. PR objectui#11848, head00304bc.-
PR shape: draft against
main, with455c646merged in (no rebase).mainhas since moved one commit (59ce048, seat 1's objectui#11790), andgit merge-treeoforigin/mainand this head is clean; the queue tests that merge. First lineFixes #11785, no other closing keyword.Clause-②: noat line start. Commits carry only the model-free trailer pair. Assigned toos-support-ai. -
Scope: 11 files, +1142/−79, on the claim's surface as amended (
6050143821):studio-design/StudioDesignSurface.tsx: the Data, Automations and Interfaces pillars' save strips (thefetchPackagestoasts are left alone, as the amendment corrected);studio-design/metadataError.ts: the refusal view model (StudioRefusal, the three per-pillar locators,plainRefusalas the unchanged fallback);- new designer rows (en, zh) in
metadata-admin/i18n.ts; data-objectstack'sobject-metadata-write-guard.ts(message text only) and the two// throws:quotes in its publishedREADME.md;- the tests beside them and the changeset (
patchon both packages).
The published
formatMetadataErroris untouched, so its other call sites print what they printed. Nothing is added to either package entry. No governed path. -
Diff read (the seat's own):
- The guard's two messages drop the door label,
@objectstack/specand the tracker ids, and now begin "The object was not saved: the field …". The field, its type and the remedy are kept. The signature is unchanged;writeris renamed_writer, a name only. - Studio tells the guard's refusal apart without parsing prose and without a new export: it re-runs the guard on the body it sent, and counts
eas that refusal only whenehas no status, no issues, and a byte-identical message. Anything else falls back toplainRefusal, which is the old verbatim text. - A server 422's issues are placed by the pillar's locator (a field label, a step label plus its inspector input, a navigation item plus its input). A path the page cannot place keeps its line under Details, with the generic sentence.
- Each strip renders one sentence, a "Show me" button when the path maps, and a closed "Details" holding exactly
formatMetadataError(e).
- The guard's two messages drop the door label,
-
README patch round (
36165fc..00304bc): exactly two lines,packages/data-objectstack/README.mdat:784and:811, each// throws:prefix now the guard's measured first line. Read by the seat on the compare. After this PR, the old prefix "refused this object metadata write" has no occurrence in the tree outside changelogs (onmain: 2 in the guard, 2 in the README). -
Ruling honoured: triage's direction on the card: an author sentence, a located field, the technical detail collapsible. The amendment's answer A (README quotes fixed in this PR) is done.
-
Changeset sentences checked against the head:
- "Each strip now shows one sentence that names the input … A "Show me" button opens that field, step or navigation item. The raw text stays under a closed "Details" disclosure, unchanged";
- "Switching a field to Picklist before it has an option now says "Changes not saved: the field “Status” needs at least one option…"";
- "
assertObjectMetadataWritableno longer prints the door's class name, a package name or tracker ids … Its signature, error type and exports are unchanged"; - "
formatMetadataErroris unchanged … Nothing is added to either package entry".
-
Reverse verification (dev report):
- Ablation through
ablation-replace.mjs: the Data page's catch put back toplainRefusal(e)gives both Data pins red (2/2), the first receiving the guard's old-style text. Restored with the blob equal to HEAD. - The derived test union on
36165fc: 228 files, 2608 tests passed. It includes the 11 plugin-designerMetadataFieldsPage*suites, which are the non-StudioformatMetadataErrorcallers and the control. - The patch round on
00304bc:check:doc-snippets(every package README) andcheck:readme-exportsexit 0 after the build; the type-check exit 0.
- Ablation through
-
Gates: CI on
00304bc(head_shaequals the PR head): 43 runs, 40 success, 3 skipped (Test (coverage), its shard template,dependabot), 0 failure.LintandType Checkare success. Mergeable stateclean. -
Contract review: not required (
Clause-②: no).
Findings:
translateValidationMessagemaps a bareRequiredto "A name is required." whatever the path, and the new sentence inherits it asResourceEditPagedoes. Not reached at a public door in this run, so it does not pass the filing gate →Acceptance notes- Publish failures stay toasts (
formatPublishFailures), and create dialogs printerror.message. Both are surfaces the card did not measure →Acceptance notes
Follow-up: objectui#11786 (the autosave refusing intermediate states) is serial behind this card and is picked once this lands.
Landing: ready + auto-merge through the queue, now.
Generated by Claude Code
-
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsLanded —
domain:uiseat 3,session_01CGZy1BGCjdN5cXqL9cnvB8, 2026-10-08T01:53Z. PR objectui#11848 merged through the queue as4a9fe31at 2026-10-08T01:51Z.- The merge:
- One parent,
59ce048, an ancestor oforigin/main. - The landed tree equals
git merge-treeof that parent and the accepted head00304bc(tree77c9270con both). - 11 files, +1142/−79.
- One parent,
- Content check,
59ce048→4a9fe31, underpackages/:- The guard's old prefix "refused this object metadata write": 4 → 0 (2 in the guard, 2 in the published README).
- "The object was not saved": 0 → 4.
StudioRefusalStrip: 0 → 5.- Control:
packages/data-objectstack/src/metadata-error.ts(formatMetadataError) is byte-identical on both sides, so its other call sites print what they printed.
- Closures: the PR's only closing keyword was
Fixes #11785. It is the only issue closed since 01:30Z.pm:dispatchedis removed. - Follow-ups: objectui#11786 (the autosave refusing intermediate states) was serial behind this card on the same save strips. Its release condition, as seat 2 wrote it in
6049737578, is now met.
Generated by Claude Code
- The merge:
- added a commit that references this issue
on Oct 9, 2026
Filing gate ① — product defect with a named location and a reproduction. reach: Studio Data pillar: switching a field's type to Picklist shows the red banner quoted below to the author, verbatim.
Who acts on it: objectui triage → the Studio / app-shell owner. ⛔ Not a claim. Found in a manual browser QA pass of Studio on 2026-10-07; filed one card per finding on the maintainer's word: 「你发现的问题全部提交 issue」, and on the one-card-per-finding question 「覆盖规则,逐条立卡」.
What happens
Text written for developers reaches low-code authors unchanged:
statusis aselectwith no options … or a sharedpicklist(objectstack#20827), so this draft stays here until it has one."@objectstack/specrefuses the same document at the server with a 422 onfields.technician.reference… (objectui#7714).".describe()docstrings rendered as form help (the notify node's Template help ends "(maintainer ruling 2026-09-01). A producer-set payload.locale is not consulted."); the action Description help ends "(one dialog, not two —)" with a dangling dash.Where it comes from (read in source)
assertObjectMetadataWritableindata-objectstack/src/object-metadata-write-guard.tsbuilds the first two messages (class name, tracker ids, wire paths);formatMetadataErrorpasses server issue paths through; inspectors render spec descriptions as help. The spec-side strings are objectstack-ai/objectstack#22093.Expected
An author-facing sentence attached to the input it is about, with the technical detail (path, code, ids) behind a disclosure.
Suggested direction (triage to rule)
Split every refusal into
{authorMessage, fieldPath, detail}; map Zod paths to the inspector field; keep tracker ids out of runtime strings (the rule objectstack's AGENTS.md already applies to its own runtime prose).Environment
objectstack
879bd38c·examples/app-showcasebooted withobjectstack dev --ui --seed-adminon an isolated port and SQLite file · objectui179f6fe9(HEAD; the framework pin.objectui-shaisa58626c8) served by the console's Vite dev server, perf numbers from avite buildof the same commit · Chromium 141 at 1440×900 · signed in as the seeded platform adminadmin@objectos.aiunless stated.Duplicate check
os lintprints a liveness row's internal ledgernote, tracker ids included, as the fix text on opted-in rows (object.externalSharingModelcites #2696 on app-showcase) objectstack#21096 (closed) — same 'no tracker ids in author-visible text' rule (AGENTS.md), different surface (CLI lint)Dedupe words: developer facing error author · MetadataClient.save refused banner · raw zod path banner · describe docstring as help text
Filed by Claude Code (session
session_01D76mrPJrSSdaKRxR2rvrMG) from that QA pass.Generated by Claude Code